tags:

views:

79

answers:

4

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?

+3  A: 

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]" />
Tomalak
Hey I m havin an extra template this time ... In the last post you had one :D
Rashmi Pandit
+2  A: 

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"&gt;
    <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.

Rashmi Pandit
+1  A: 

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

santiiiii
+1  A: 

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"&gt;

  <!-- 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"/>
Evan Lenz