Positional grouping using XSLT -


i have xml contain documents:

<document>     <line id="0">         <field id="0"><![cdata[h:doc1]]></field>     </line>     <line id="1">         <field id="0"><![cdata[l:1]]></field>     </line>     <line id="2">         <field id="0"><![cdata[l:2]]></field>     </line>     <line id="3">         <field id="0"><![cdata[l:3]]></field>     </line>     <line id="4">         <field id="0"><![cdata[h:doc2]]></field>     </line>     <line id="5">         <field id="0"><![cdata[l:1]]></field>     </line>  </document> 

h=header of document , l=line-item. in example whe have 2 h means 2 documents number doc1 , doc2. doc1 have 3 line items , doc2 have 1 line item.

how convert data using xslt version 1 result:

<documents>     <document>         <header>             <number>doc1</number>         </header>         <line-item>             <line-number>1</line-number>             <line-number>2</line-number>             <line-number>3</line-number>         </line-item>     </document>     <document>         <header>             <number>doc2</number>         </header>         <line-item>             <line-number>1</line-number>         </line-item>     </document> </documents> 

this xslt 1.0 transformation:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/xsl/transform">  <xsl:output omit-xml-declaration="yes" indent="yes"/>  <xsl:strip-space elements="*"/>   <xsl:key name="kfollowing"    match="line[starts-with(field,'l:')]"    use="generate-id(preceding-sibling::line                          [starts-with(field,'h:')]                          [1]                     )"/>   <xsl:template match="/">      <documents>       <xsl:apply-templates/>      </documents>  </xsl:template>   <xsl:template match="line[starts-with(field,'h:')]">   <document>    <header>     <number><xsl:value-of select="substring-after(field,'h:')"/></number>     <line-item>      <xsl:apply-templates mode="ingroup" select=         "key('kfollowing', generate-id())"/>     </line-item>    </header>   </document>  </xsl:template>   <xsl:template match="line" mode="ingroup">   <line-number>    <xsl:value-of select="substring-after(field,'l:')"/>   </line-number>  </xsl:template>  <xsl:template match="text()"/> </xsl:stylesheet> 

when applied on provided xml document:

<document>     <line id="0">         <field id="0"><![cdata[h:doc1]]></field>     </line>     <line id="1">         <field id="0"><![cdata[l:1]]></field>     </line>     <line id="2">         <field id="0"><![cdata[l:2]]></field>     </line>     <line id="3">         <field id="0"><![cdata[l:3]]></field>     </line>     <line id="4">         <field id="0"><![cdata[h:doc2]]></field>     </line>     <line id="5">         <field id="0"><![cdata[l:1]]></field>     </line> </document> 

produces wanted, correct result:

<documents>    <document>       <header>          <number>doc1</number>          <line-item>             <line-number>1</line-number>             <line-number>2</line-number>             <line-number>3</line-number>          </line-item>       </header>    </document>    <document>       <header>          <number>doc2</number>          <line-item>             <line-number>1</line-number>          </line-item>       </header>    </document> </documents> 

explanation: using keys conveniently specify , select complete group of adjacent "lines" following "header".


Comments

Popular posts from this blog

c# - How to set Z index when using WPF DrawingContext? -

razor - Is this a bug in WebMatrix PageData? -

visual c++ - Using relative values in array sorting ( asm ) -