Welcome to WebmasterWorld Guest from 54.145.221.99

Forum Moderators: httpwebwitch

Message Too Old, No Replies

Sorting Out Elements, Based on Position, Relative to Siblings

An annoying XSLT conundrum

   
5:45 pm on Jun 10, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm running into an annoying wall, parsing MS Project XML. When exported from Project itself, no problem. However, I have another app that exports project files, and it isn't so easy to transform. This isn't urgent, as all I need to do is open and re-save from Project, but I'd shore like to be able to transform it all.

Here's a simplified example of the type of (non-Project) XML I'd like to transform:

<sibling level="1"></sibling> 
<sibling level="1"></sibling>
<sibling level="2"></sibling>
<sibling level="2"></sibling>
<sibling level="3"></sibling>
<sibling level="1"></sibling>
<sibling level="1"></sibling>
<sibling level="2"></sibling>
<sibling level="1"></sibling>

Level 2 siblings actually belong inside the preceding Level 1 sibling, and Level 3 ones go inside Level 2, etc:

<sibling level="1"></sibling> 
<sibling level="1">
<sibling level="2"></sibling>
<sibling level="2">
<sibling level="3"></sibling>
</sibling>
</sibling>
<sibling level="1"></sibling>
<sibling level="1">
<sibling level="2"></sibling>
</sibling>
<sibling level="1"></sibling>

Now, XSLT doesn't have a way to break a for-each loop, which totally scrags the way I'd do it in C++ or PHP (Keep going until you hit a higher level), so that means that I should use XPath to create appropriate nodesets.

The problem is that I can't actually figure out a way to do this easily. I'm using a recursive template to create the nested XHTML from the XML, but the darn thing keeps getting ALL of the Level 2 siblings, when I only want the first ones to be considered.

Any ideas?

7:37 pm on Jun 10, 2008 (gmt 0)

WebmasterWorld Administrator httpwebwitch is a WebmasterWorld Top Contributor of All Time 10+ Year Member



oy, cm! you can't just post a simple one can you? :P

intuitively, this seems like something XSLT should be able to do. I don't know how, but my gut feeling is this problem isn't "way out there" like trying to translate from English to Swahili using array keys, or drawing trigonometry graphs using ascii art.

This isn't urgent

well that's a good thing.
I'll give it a shot... and I'll probably have to ask around among some other XSLT cogniscienti
7:42 pm on Jun 10, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



this seems like something XSLT should be able to do.

I know that feeling. I may have my head stuck too far up my procedural programming space. That tends to be my biggest blocker with XSLT.

Thanks for any help.

12:30 am on Jun 11, 2008 (gmt 0)

5+ Year Member



What a beautiful problem! 3 hours of playing and viola!

XML File:


<?xml version="1.0" encoding="utf-8" ?>
<siblings>
<sibling level="1"></sibling>
<sibling level="1"></sibling>
<sibling level="2"></sibling>
<sibling level="2"></sibling>
<sibling level="3"></sibling>
<sibling level="2"></sibling>
<sibling level="1"></sibling>
<sibling level="1"></sibling>
<sibling level="2"></sibling>
<sibling level="1"></sibling>
</siblings>

XSL File:

[1]
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="utf-8"/>
<xsl:template match="/">
<siblings>
<xsl:apply-templates select="siblings/sibling[1]" mode="list-item"/>
</siblings>
</xsl:template>
<xsl:template match="sibling" mode="list-item">
<xsl:param name="level" select="1"/>
<xsl:param name="position" select="1"/>
<xsl:if test="number(@level)=$level">
<sibling>
<xsl:for-each select="@*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates select="following-sibling::sibling[1]" mode="list-item">
<xsl:with-param name="level" select="$level+1"/>
<xsl:with-param name="position" select="$position+1"/>
</xsl:apply-templates>
</sibling>
<xsl:apply-templates select="following-sibling::sibling[1]" mode="list-sibling">
<xsl:with-param name="level" select="$level"/>
<xsl:with-param name="position" select="$position+1"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template match="sibling" mode="list-sibling">
<xsl:param name="level" select="1"/>
<xsl:param name="position" select="1"/>
<xsl:choose>
<xsl:when test="number(@level)&gt;$level">
<xsl:apply-templates select="following-sibling::sibling[1]" mode="list-sibling">
<xsl:with-param name="level" select="$level"/>
<xsl:with-param name="position" select="$position+1"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="number(@level)=$level">
<xsl:apply-templates select="." mode="list-item">
<xsl:with-param name="level" select="$level"/>
<xsl:with-param name="position" select="$position"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise><!-- Terminate recursion --></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Really enjoyed it! Thanks! I hope my solution is adequate and on par with your standards.

Cheers,
Ivan

[edited by: httpwebwitch at 12:15 pm (utc) on June 11, 2008]
[edit reason] added formatting to prevent text formatt - I'll sticky you with explanation [/edit]

12:57 am on Jun 11, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Looks like it makes sense. I'll give it a whack, and see what happens.

My standards? Right now, it's hacking at something until it looks roughly akin to what I want.

I don't think it will be a problem.

I'll post any tweaks or improvements here.

Thanks!

By the way, what a great inaugural post!

Welcome to WebmasterWorld!

5:27 am on Jun 11, 2008 (gmt 0)

WebmasterWorld Administrator httpwebwitch is a WebmasterWorld Top Contributor of All Time 10+ Year Member



Welcome to WebmasterWorld, mrVano!
It's great to have another XSLT expert aboard. Go into the Control Panel, click "Forum Watch List", and subscribe to this forum (and others, if you like). Then you'll get an email alert whenever a new topic is started here.

XML is a quiet topic compared to others like PHP and Javascript. But still, when someone needs help with XML, XSLT, XPATH, etc., this is the place.

Before diving in too far, stop and actually read the TOS (we do take it seriously, to the letter, and it is enforced - link is in the footer)

Cheers
Ian

2:05 pm on Jun 11, 2008 (gmt 0)

WebmasterWorld Administrator httpwebwitch is a WebmasterWorld Top Contributor of All Time 10+ Year Member



cm: I tried mrVano's solution in oXygen, and indeed - it works!
3:20 pm on Jun 11, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



It works.

I had to mangle it a bit to fit the real world implementation, but the stylesheet works.

Thanks, Ivan!