Tutorial :Move sub nodes into parent attributes with XSLT



Question:

I have some XML which contains records and sub records, like this:

<data>      <record jsxid="id0x0b60fec0" ID="12429070" Created="2008-10-21T03:00:00.0000000-07:00">          <record jsxid="id0x0b60ff10" string="101"/>          <record jsxid="id0x0e64d8e8" string="63"/>          <record jsxid="id0x2fd83f08" string="Y"/>      </record>      <record jsxid="id0x0b60fec0" ID="12429070" Created="2008-10-21T03:00:00.0000000-07:00">          <record jsxid="id0x0b60ff10" string="102"/>          <record jsxid="id0x0e64d8e8" string="77"/>          <record jsxid="id0x2fd83f08" string="Y"/>      </record>         <data>  

I need to transform it so that the string attribute of the sub records are brought up into the parent record as consecutively numbered attributes and then discarded, like this:

<data>      <record jsxid="id0x0b60fec0" ID="12429070" Created="2008-10-21T03:00:00.0000000-07:00" 1="101" 2="63" 3="Y"/>      <record jsxid="id0x0b60fec0" ID="12429070" Created="2008-10-21T03:00:00.0000000-07:00" 1="102" 2="77" 3="Y"/>  <data>  

The number of sub-records is arbitrary across documents but remains static within the same document.

Would someone be so kind as to point the way to an XSLT solution? Many thanks.


Solution:1

Here's a complete solution:

<xsl:stylesheet version="1.0"    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <!-- By default, recursively copy all nodes unchanged -->    <xsl:template match="@* | node()">      <xsl:copy>        <xsl:apply-templates select="@* | node()"/>      </xsl:copy>    </xsl:template>      <!-- But don't process any children of <record> (such as whitespace)... -->    <xsl:template match="record/node()"/>      <!-- ...except for doubly-nested records;         convert them to attributes, named according to position -->    <xsl:template match="record/record" priority="1">      <xsl:variable name="pos">        <xsl:number/>      </xsl:variable>      <xsl:attribute name="r{$pos}">        <xsl:value-of select="@string"/>      </xsl:attribute>    </xsl:template>    </xsl:stylesheet>  

Note that I changed the name of your attributes to "r1", "r2", etc., because XML doesn't allow you to start a name with a number.


Solution:2

This might do it, run this following snippet of XSLT when processing the top level <record> elements:

<xsl:for-each select="record">      <xsl:attribute name="{position()}">          <xsl:value-of select="@string" />      </xsl:attribute>  </xsl:for-each>  

Essentially this iterates over each sub-<record> element and creates an <xsl:attribute> element describing the desired attribute. The position() function is called to get the relative position within the top level element: 1, 2, 3, etc.

This is not a complete solution; some familiarity with XSLT is assumed.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »