0

I have an XML document that has several <item> elements. Inside each, there might be one or more of the following elements: <list>, <listAfter>, and <listBefore>. So, ignoring a lot of the extraneous elements, it might look like this:

<items>
  <item>
    <!-- ... various elements ... -->
    <list>Enhancements</list>
  </item>
  <item>
    <!-- ... various elements ... -->
    <listBefore>Enhancements</listBefore>
    <listAfter>Bugs</listAfter>
  </item>
  <item>
    <!-- ... various elements ... -->
    <list>Enhancements</list>
    <listAfter>Next Release</listAfter>
  </item>
  <item>
    <!-- ... various elements ... -->
    <listBefore>Bugs</listBefore>
  </item>
  <item>
    <!-- ... various elements ... -->
  </item>
</items>

I want to remove all of the extraneous <list*> elements and have one <list> element per <item>. That element's value should follow this logic:

  • Use the value of <list> if it's available.
  • Otherwise, use the value of <listAfter> if it's available.
  • Otherwise, use the value of <listBefore> if it's available.
  • If none of these fields exist, use No List as the value.

Using my XML document above, here's what I would expect the output to look like:

<items>
  <item>
    <!-- ... various elements ... -->
    <list>Enhancements</list>
  </item>
  <item>
    <!-- ... various elements ... -->
    <list>Bugs</list>
  </item>
  <item>
    <!-- ... various elements ... -->
    <list>Enhancements</list>
  </item>
  <item>
    <!-- ... various elements ... -->
    <list>Bugs</list>
  </item>
  <item>
    <!-- ... various elements ... -->
    <list>No List</list>
  </item>
</items>

Other than using the Identity Transform to copy over all other elements, I'm not sure how to include this logic in a nice manner. As always, your help is much appreciated.

1 Answer 1

1

You could do this by overriding the identity template, and adding extra templates to match the criteria for the various list elements.

To match listAfter you wish to include in the output, you would do the following (i.e listAfter elements with no list element as a sibling)

<xsl:template 
   match="listAfter[not(preceding-sibling::list|following-sibling::list)]">

For the listBefore, you need to match them only if they have neither list not listAfter elements as siblings

<xsl:template 
  match="listBefore[not(
    preceding-sibling::list|following-sibling::list
    |preceding-sibling::listAfter|following-sibling::listAfter)]">

In other cases, you would ignore listAfter and listBefore elements:

<xsl:template match="listAfter|listBefore" />

Finally, you can match item elements with none of the various list elements as children like so:

<xsl:template match="item[not(list|listAfter|listBefore)]">

So, given the following XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes" />

   <xsl:template match="listAfter[not(preceding-sibling::list|following-sibling::list)]">
      <list>
      <xsl:apply-templates select="@*|node()"/>
      </list>
   </xsl:template>

   <xsl:template match="listBefore[not(preceding-sibling::list|following-sibling::list|preceding-sibling::listAfter|following-sibling::listAfter)]">
      <list>
      <xsl:apply-templates select="@*|node()"/>
      </list>
   </xsl:template>   

   <xsl:template match="listAfter|listBefore" />

   <xsl:template match="item[not(list|listAfter|listBefore)]">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <list>No List</list>
    </xsl:copy>
  </xsl:template>

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

When this is applied to your source XML, the following is output:

<items>
   <item>
      <!-- ... various elements ... -->
      <list>Enhancements</list>
   </item>
   <item>
      <!-- ... various elements ... -->
      <list>Bugs</list>
   </item>
   <item>
      <!-- ... various elements ... -->
      <list>Enhancements</list>
   </item>
   <item>
      <!-- ... various elements ... -->
      <list>Bugs</list>
   </item>
   <item>
      <!-- ... various elements ... -->
      <list>No List</list>
   </item>
</items>
Sign up to request clarification or add additional context in comments.

1 Comment

Awesome. I'm still getting used to thinking in the right frame of mind for XSLT; thanks for your help!

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.