Altova Mailing List Archives>Archive Index >microsoft.public.xsl Archive Home >Recent entries >Thread Prev - Re: Converting a very flat simple xml to hierarchy xml [Thread Next] Re: Converting a very flat simple xml to hierarchy xmlTo: NULL Date: 5/2/2007 7:12:00 PM
On May 2, 2:08 pm, "M" <m...@m.com> wrote:
> Hi Craig,
>
> It's quite a common requirement [ I don't know who all these people are that
> keep producing flat XML - but I wish they'd stop it! :) ]
>
> You need to look for following-siblings at the start of each hierarchy - but
> then filter those following siblings down to only those where the immediate
> preceding sibling (e.g. preceding-sibling::*[1] - because 1 in a reverse
> axis is the closest) with the correct name is the element that started this
> branch.
>
> Assuming that your elements aren't really called <a>, <b> and <c> and so
> on - and that you've given that merely as an example of your real data. And
> assuming that the text nodes in your example are there only for
> demonstration purposes - rather than providing any assistance in the 'quest'
>
> Let's try making it static-data driven - so all you have to modify is the
> static data to make it work with your real-life XML. Perhaps something
> like...
>
> == XSL1 ======================================
> <?xml version="1.0"?>
> <xsl:stylesheet version="1.0"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> xmlns:staticdata="urn:my-static-data">
>
> <xsl:output method="xml" indent="yes"/>
>
> <staticdata:hierarchy>
> <element name="a"/>
> <element name="b"/>
> <element name="c"/>
> <element name="d"/>
> </staticdata:hierarchy>
> <xsl:variable name="gvHierarchy"
> select="document('')/*/staticdata:hierarchy/element/@name"/>
>
> <xsl:template match="/">
> <xsl:apply-templates/>
> </xsl:template>
>
> <xsl:template match="/*">
> <xsl:copy>
> <xsl:copy-of select="@*"/>
> <xsl:apply-templates select="*[local-name() = $gvHierarchy[1]]"
> mode="hierarchic"/>
> </xsl:copy>
> </xsl:template>
>
> <xsl:template match="*" mode="hierarchic">
> <xsl:param name="pHierarchyLevel" select="1"/>
> <xsl:variable name="vThisElementID" select="generate-id()"/>
> <xsl:copy>
> <xsl:copy-of select="@* | text()"/>
> <xsl:apply-templates select="following-sibling::*[local-name() =
> $gvHierarchy[number($pHierarchyLevel +
> 1)]][generate-id(preceding-sibling::*[local-name() =
> $gvHierarchy[number($pHierarchyLevel)]][1]) = $vThisElementID]"
> mode="hierarchic">
> <xsl:with-param name="pHierarchyLevel" select="$pHierarchyLevel + 1"/>
> </xsl:apply-templates>
> </xsl:copy>
> </xsl:template>
> </xsl:stylesheet>
> =============================================
>
> Hmmm, say then that we didn't know what element names were going to be
> thrown at us - we'd need to build that list of names dynamically. Something
> like...
>
> == XSL2 ======================================
> <?xml version="1.0"?>
> <xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> xmlns:msxsl="urn:schemas-microsoft-com:xslt">
>
> <xsl:output method="xml" indent="yes"/>
>
> <xsl:key name="kDistinctRootChildElementNames" match="/*/*"
> use="local-name()"/>
> <xsl:variable name="rtf_Hierarchy">
> <xsl:for-each select="/*/*[generate-id() =
> generate-id(key('kDistinctRootChildElementNames',local-name()))]">
> <element name="{local-name()}"/>
> </xsl:for-each>
> </xsl:variable>
> <xsl:variable name="gvHierarchy"
> select="msxsl:node-set($rtf_Hierarchy)/element/@name"/>
>
> <xsl:template match="/">
> <xsl:apply-templates/>
> </xsl:template>
>
> <xsl:template match="/*">
> <xsl:copy>
> <xsl:copy-of select="@*"/>
> <xsl:apply-templates select="*[local-name() = $gvHierarchy[1]]"
> mode="hierarchic"/>
> </xsl:copy>
> </xsl:template>
>
> <xsl:template match="*" mode="hierarchic">
> <xsl:param name="pHierarchyLevel" select="1"/>
> <xsl:variable name="vThisElementID" select="generate-id()"/>
> <xsl:copy>
> <xsl:copy-of select="@* | text()"/>
> <xsl:apply-templates select="following-sibling::*[local-name() =
> $gvHierarchy[number($pHierarchyLevel +
> 1)]][generate-id(preceding-sibling::*[local-name() =
> $gvHierarchy[number($pHierarchyLevel)]][1]) = $vThisElementID]"
> mode="hierarchic">
> <xsl:with-param name="pHierarchyLevel" select="$pHierarchyLevel + 1"/>
> </xsl:apply-templates>
> </xsl:copy>
> </xsl:template>
> </xsl:stylesheet>
> =============================================
>
> Then, perhaps to get really fancy - we could provide a mechanism that would
> allow us to override the behaviour for certain conditions. Maybe...
>
> == XSL2 ======================================
> <?xml version="1.0"?>
> <xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> xmlns:msxsl="urn:schemas-microsoft-com:xslt">
>
> <xsl:output method="xml" indent="yes"/>
>
> <xsl:key name="kDistinctRootChildElementNames" match="/*/*"
> use="local-name()"/>
> <xsl:variable name="rtf_Hierarchy">
> <xsl:for-each select="/*/*[generate-id() =
> generate-id(key('kDistinctRootChildElementNames',local-name()))]">
> <element name="{local-name()}"/>
> </xsl:for-each>
> </xsl:variable>
> <xsl:variable name="gvHierarchy"
> select="msxsl:node-set($rtf_Hierarchy)/element/@name"/>
>
> <xsl:template match="/">
> <xsl:apply-templates/>
> </xsl:template>
>
> <xsl:template match="/*">
> <xsl:copy>
> <xsl:copy-of select="@*"/>
> <xsl:apply-templates select="*[local-name() = $gvHierarchy[1]]"
> mode="hierarchic"/>
> </xsl:copy>
> </xsl:template>
>
> <xsl:template match="*" mode="hierarchic">
> <xsl:param name="pHierarchyLevel" select="1"/>
> <xsl:variable name="vThisElementID" select="generate-id()"/>
> <xsl:variable name="vChildren" select="following-sibling::*[local-name() =
> $gvHierarchy[number($pHierarchyLevel +
> 1)]][generate-id(preceding-sibling::*[local-name() =
> $gvHierarchy[number($pHierarchyLevel)]][1]) = $vThisElementID]"/>
> <xsl:apply-templates select="." mode="process">
> <xsl:with-param name="pChildren" select="$vChildren"/>
> <xsl:with-param name="pHierarchyLevel" select="$pHierarchyLevel"/>
> </xsl:apply-templates>
> </xsl:template>
>
> <xsl:template match="*" mode="process">
> <xsl:param name="pChildren"/>
> <xsl:param name="pHierarchyLevel"/>
> <xsl:choose>
> <xsl:when test="$pHierarchyLevel = count($gvHierarchy)">
> <leaf type="{local-name()}" value="{text()}"/>
> </xsl:when>
> <xsl:otherwise>
> <folder type="{local-name()}" value="{text()}">
> <xsl:apply-templates select="$pChildren" mode="hierarchic">
> <xsl:with-param name="pHierarchyLevel" select="$pHierarchyLevel + 1"/>
> </xsl:apply-templates>
> </folder>
> </xsl:otherwise>
> </xsl:choose>
> </xsl:template>
>
> <!-- do something special for any <a> elements we come across -->
> <xsl:template match="a" mode="process">
> <xsl:param name="pChildren"/>
> <xsl:param name="pHierarchyLevel"/>
> <the_big_cahunA value="{text()}">
> <xsl:apply-templates select="$pChildren" mode="hierarchic">
> <xsl:with-param name="pHierarchyLevel" select="$pHierarchyLevel + 1"/>
> </xsl:apply-templates>
> </the_big_cahunA>
> </xsl:template>
> </xsl:stylesheet>
> =============================================
>
> All of these will strip any comments or processing instructions that may be
> present in the input. If you wanted to preserve those you'd have a little
> additional work to do.
>
> HTH
> M<craig...@gmail.com> wrote in message
>
> news:1178079533.186121.232530@y......
>
> > Been trying for a long time to make this work. Here's my starting xml:
>
> > <root>
> > <a>top 1</a>
> > <b>b 110</b>
> > <b>b 111</b>
> > <b>b 112</b>
> > <c>c 11</c>
> > <b>b 120</b>
> > <b>b 121</b>
> > <c>c 12</c>
> > <b>b 130</b>
> > <b>b 131</b>
> > <b>b 132</b>
> > <b>b 133</b>
> > <c>c 13</c>
> > <b>b 140</b>
> > <c>c 14</c>
> > <a>top 2</a>
> > <b>b 210</b>
> > <b>b 211</b>
> > <b>b 212</b>
> > <c>c 21</c>
> > <b>b 220</b>
> > <b>b 221</b>
> > <c>c 22</c>
> > </root>
>
> > And here is the xml I want to end up:
>
> > <root>
> > <a>top 1
> > <c>c 11
> > <b>b 110</b>
> > <b>b 111</b>
> > <b>b 112</b>
> > </c>
> > <c>c 12
> > <b>b 120</b>
> > <b>b 121</b>
> > </c>
> > <c>c 13
> > <b>b 130</b>
> > <b>b 131</b>
> > <b>b 132</b>
> > <b>b 133</b>
> > </c>
> > <c>c 14
> > <b>b 140</b>
> > </c>
> > </a>
> > <a>top 2
> > <c>c 21
> > <b>b 210</b>
> > <b>b 211</b>
> > <b>b 212</b>
> > </c>
> > <c>c 22
> > <b>b 220</b>
> > <b>b 221</b>
> > </c>
> > </a>
> > </root>
>
> > I've tried a lot of things. It's hard to move the c element up to be a
> > parent of b.
>
> > Thanks if anyone can figure it out :)
>
> > Craig
Wow you guys are awesome! Thanks a lot for your help. This is exactly
what I needed :)
Craig
| ||||||
| Company | Legal | Press | Partners | Careers | Sitemap | Contact Us | Altova Blog | Mobile | Full Site | |||
|
