Tutorial :XSLT to transform a series of elements into a single list?



Question:

Given a source doc like so:

<A>    <B>      <C>item1</C>    </B>    <B>      <C>item2</C>    </B>    <B>      <C>item3</C>    </B>  </A>  

What XSLT can I use to product something like this:

<A>    <B>      <C>item1</C>      <C>item2</C>      <C>item3</C>    </B>  </A>  

I have tried a sheet with...

  <xsl:template match="B">      <xsl:choose>        <xsl:when test="count(preceding-sibling::B)=0">          <B>            <xsl:apply-templates select="./C"/>            <xsl:apply-templates select="following-sibling::B"/>          </B>        </xsl:when>          <xsl:otherwise>            <xsl:apply-templates select="./C"/>        </xsl:otherwise>        </xsl:choose>    </xsl:template>  

but I am getting...

<A>    <B>      <C>item1</C>      <C>item2</C>      <C>item3</C>    </B>    <C>item2</C>    <C>item3</C>  </A>  

A second question: I have a hard time debugging XSLT. hints?


Solution:1

Simplest approach:

<xsl:template match="/A">    <A>      <B>        <xsl:copy-of select=".//C" />      </B>    </A>  </xsl:template>  

To answer the question why you see the output you see with your XSLT:

I aussume that you have a

<xsl:apply-templates select="B" />  

in place. This means:

  • The <xsl:template match="B"> is called three times, once for each <B>.
  • For the first <B>, it does what you intend, the other times it branches right into the <xsl:otherwise>, copying the <C>s via the <xsl:template match="C"> you probably have. This is where your extra <C>s come from.
  • To fix it (not that I endorse your approach), you should change it like...

...this:

<xsl:apply-templates select="B[1]" />  


Solution:2

You can use Altova XmlSpy or Visual Studio for debugging. The following xslt will give desired o/p.

<?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" version="1.0" encoding="UTF-8" indent="yes"/>      <xsl:template match="A">          <A>              <B>                  <xsl:apply-templates/>              </B>          </A>      </xsl:template>      <xsl:template match="B">          <xsl:copy-of select="C"/>      </xsl:template>  </xsl:stylesheet>  

If the above soln does not work for you (I suspect you are dealing with a more complex xml), you might want to post some more info on your xml.

Also, having templates for B and C would enable further extension of template o/p, if reqd.


Solution:3

To answer your second question, I use the VS 2008 debugger, and it works like a charm.


Solution:4

This stylesheet merges sibling <B> elements, regardless of location in the stylesheet or what the other element names are.

<xsl:stylesheet version="1.0"    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <!-- By default, copy all nodes unchanged -->    <xsl:template match="@* | node()">      <xsl:copy>        <xsl:apply-templates select="@* | node()"/>      </xsl:copy>    </xsl:template>       <!-- Only copy the first B element -->    <xsl:template match="B[1]">      <xsl:copy>        <!-- Merge the attributes and content of all sibling B elements -->        <xsl:apply-templates select="../B/@* |                                     ../B/node()"/>      </xsl:copy>    </xsl:template>      <!-- Don't copy the rest of the B elements -->    <xsl:template match="B"/>    </xsl:stylesheet>  

UPDATE:

If you want the result to be pretty-printed, and if whitespace-only text nodes are insignificant in your input document, then you could add this to the top of your stylesheet (as children of <xsl:stylesheet>):

<xsl:strip-space elements="*"/>  <xsl:output indent="yes"/>  

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