2

How can I transform nested XML elements with xslt, keeping the structure?

Let's say I have an XML document like this:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <node>
  </node>
  <node>
    <node>
      <node>
      </node>
    </node>
  </node>
</root>

And I would like to get something like this:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <element>
    </element>
    <element>
      <element>
        <element>
        </element>
      </element>
    </element>
</root>

What kind of xslt should I use?

Thanks!

4
  • 1
    "What kind"? What does that even mean? Commented Oct 11, 2010 at 10:23
  • The second example is not valid XML, as there are 2 root elements. You can only have one root element in a valid XML document. Commented Oct 11, 2010 at 10:27
  • @Ignacio: I mean could you help me to write an xslt wich works with the sample xmls? @Oded: You are right, I fixed. Commented Oct 11, 2010 at 10:35
  • Good question, +1. See my answer for the classic, most "in the XSLT spirit" solution that uses the identity rule and pure push style. :) Commented Oct 11, 2010 at 12:41

2 Answers 2

5

The way to do this in XSLT is this (using the identity rule and push style):

<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:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="node">
  <element>
    <xsl:apply-templates select="node()|@*"/>
  </element>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<root>
  <node>
  </node>
  <node>
    <node>
      <node>
      </node>
    </node>
  </node>
</root>

the wanted, correct result is produced:

<root>
   <element/>
   <element>
      <element>
         <element/>
      </element>
   </element>
</root>

Do note:

  1. The use of the identity rule and its overriding for only a specific element -- this is the most fundamental and powerful XSLT design pattern.

  2. How by using 1. above we achieve ellegant and pure "push style" transformation.

Sign up to request clarification or add additional context in comments.

4 Comments

I know this is an older answer, but I'm curious: why does the <xsl:template match="node"> template specify node()|@* as the selector for <xsl:apply-templates>? As far as I can tell, a straight <xsl:apply-templates /> accomplishes the same thing (since the Identity Template already specifies that selector).
@ABach: The power of the identity rule is to use it without modifications -- in this particular case there are no attributes, but tomorrow someone could change the format of the XML documents and add some attributes. If you have adhered to the exact identity rule, you don't have to change even a single character -- everything will continue to work as expected.
Ah, I see - I didn't consider that using <xsl:apply-templates /> at that level won't capture attributes in children/grandchildren/etc. nodes. Thanks, @Dimitre!
@ABach: You are welcome. You can read more about the identity rule here: dpawson.co.uk/xsl/sect2/identity.html
3
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/root">
    <root>
      <xsl:apply-templates />
    </root>
  </xsl:template>
  <xsl:template match="node">
    <element>
      <xsl:apply-templates />
    </element>
  </xsl:template>
</xsl:stylesheet>

The key is the apply-templates tag to process the tag contents recursively.

1 Comment

Good answer. You probably would be interested to know about the identity rule design pattern -- see my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.