tags:

views:

201

answers:

1

I am new to XSLT. I want to combine two nodes base on the same element value.

Here is input file.

<data>
  <node>
    <itemA element1="a" element2="x">
    <itemBs><other elements with element2=x></itemBs>
  </node>
    <itemA element1="a" element2="y">
    <itemBs><other elements with element2=y></itemBs>
  <node>
    <itemA element1="b" element2="z">
    <itemBs><other elements with element2=z></itemBs>
  </node>
  <node>
    <itemA element1="b" element2="w">
    <itemBs><other elements with element2=w></itemBs>
  </node>
</data>


I want to combine nodes with element1 has the same value. In addition, I only want to keep node itemBs with the max value of element2.

Say x>y, z>w

Here is the output I want

<data>
  <node>
    <itemAs>
      <itemA element1="a" element2="x">
      <itemA element1="a" element2="y">
    </itemAs>
    <itemBs>
      <itemBs><other elements with element2=x></itemBs>
    </itemBs>
  </node>
  <node>
    <itemAs>
      <itemA element1="a" element2="z">
      <itemA element1="a" element2="w">
    </itemAs>
    <itemBs>
      <itemBs><other elements with element2=z></itemBs>
    </itemBs>
  </node>
</data>


A: 

So, it seems I have understood your intent.

With this input:

<data>
  <node>
    <itemA element1="a" element2="x" />
    <itemBs><!-- other elements with element2=x --></itemBs>
  </node>
  <node>
    <itemA element1="a" element2="y" />
    <itemBs><!-- other elements with element2=y --></itemBs>
  </node>
  <node>
    <itemA element1="b" element2="z" />
    <itemBs><!-- other elements with element2=z --></itemBs>
  </node>
  <node>
    <itemA element1="b" element2="w" />
    <itemBs><!-- other elements with element2=w --></itemBs>
  </node>
</data>

And this (quite heavily commented) XSLT 1.0 approach:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />

  <!-- prepare a key to index "itemA" elements by "@element1" -->
  <xsl:key name="kItemA"  match="itemA" use="@element1" />

  <!-- the "data" element is going to be copied only -->
  <xsl:template match="data">
    <xsl:copy>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <!-- "node" elements need special treatment -->
  <xsl:template match="node">
    <!-- retrieve a node-set of all "itemA" with the same value -->
    <xsl:variable name="itemAList" select="key('kItemA', itemA/@element1)" />

    <!-- do the following only once (for the first node in the set) -->
    <xsl:if test="generate-id(itemA[1]) = generate-id($itemAList[1])">
      <!-- copy the "node" element to the output -->
      <xsl:copy>
        <!-- copy all "itemA" elements -->
        <xsl:copy-of select="$itemAList" />

        <!-- for-each is just a vehicle to apply the desired order -->
        <xsl:for-each select="$itemAList">
          <xsl:sort select="@element2" data-type="text" order="descending" />
          <!-- when sorted descending by @element2, use first node only -->
          <xsl:if test="position() = 1">
            <!-- copy the associated "itemBs" -->
            <xsl:copy-of select="../itemBs" />
          </xsl:if>
        </xsl:for-each>
      </xsl:copy>
    </xsl:if>
  </xsl:template>

  <!-- stray text nodes are to be ignored -->      
  <xsl:template match="text()" />

</xsl:stylesheet>

I get:

<data>
  <node>
    <itemA element1="a" element2="x" />
    <itemA element1="a" element2="y" />
    <itemBs>
      <!-- other elements with element2=y -->
    </itemBs>
  </node>
  <node>
    <itemA element1="b" element2="z" />
    <itemA element1="b" element2="w" />
    <itemBs>
      <!-- other elements with element2=z -->
    </itemBs>
  </node>
</data>

The above will be a bit hard to digest for an XSLT newbie. Ask if you have questions.

Tomalak