Question

The input xml is sorted by each element's id and I am going through and checking if the current node has the same id as the preceding sibling. If they are the same, then I would like to remove(not display) the current node from the xml and add some of its values to that sibling. It would be very helpful, to check the current node against any previous node and perform the same logic if the ids match.

Here is input xml:

<?xml version="1.0"?>
<transactions>
    <transaction>
        <name>Test Entry</name>
        <id>123</id>
        <line_memo>memo1</line_memo>
        <amount>5</amount>
    </transaction>
    <transaction>
        <name>Test 1</name>
        <id>123</id>
        <line_memo>memo2</line_memo>
        <amount>10</amount>
    </transaction>
    <transaction>
        <name>Test 2</name>
        <id>123</id>
        <line_memo>memo3</line_memo>
        <amount>15</amount>
    </transaction>
    <transaction>
        <name>Test 3</name>
        <id>1235</id>
        <line_memo>0123456789</line_memo>
        <amount>30</amount>
    </transaction>
</transactions>

MY XSLT - I have been experimenting with apply templates and params.:

    <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="transactions">
        <transactions>
        <xsl:for-each select="transaction">
            <xsl:variable name="name" select="adjustment_flag"/>
            <xsl:variable name="id" select="id"/>
            <xsl:variable name="amount" select="amount"/>
            <xsl:variable name="line_memo" select="control_amount"/>
            <xsl:variable name="invoice_date" select="invoice_number"/>

            <xsl:choose>
                <xsl:when test="(position() > 1)">
                    <xsl:choose>
                        <xsl:when test="preceding-sibling::transaction[id = $id]">
                          <transaction>
                            <xsl:apply-templates select="preceding-sibling::transaction/line_memo">
                <xsl:with-param name="newMemo" select="line_memo"/>
            </xsl:apply-templates>
                            <xsl:apply-templates select="preceding-sibling::transaction/amount">
                              <xsl:with-param name="newAmount" select="amount"/>
            </xsl:apply-templates>
                          <name><xsl:value-of select="name"/></name>
                          <id><xsl:value-of select="id"/></id>
                          <!--<line_memo><xsl:value-of select="line_memo"/></line_memo>
                          <amount><xsl:value-of select="amount"/></amount>-->
                          </transaction>
                        </xsl:when>
                        <xsl:otherwise>
                          <transaction>
                          <name><xsl:value-of select="name"/></name>
                          <id><xsl:value-of select="id"/></id>
                          <line_memo><xsl:value-of select="line_memo"/></line_memo>
                          <amount><xsl:value-of select="amount"/></amount>
                          </transaction>
                        </xsl:otherwise>
                    </xsl:choose>       
                </xsl:when>         

                <xsl:otherwise>
                  <transaction>
                  <name><xsl:value-of select="name"/></name>
                  <id><xsl:value-of select="id"/></id>
                  <line_memo><xsl:value-of select="line_memo"/></line_memo>
                  <amount><xsl:value-of select="amount"/></amount>
                  </transaction>
                </xsl:otherwise>
            </xsl:choose>               

        </xsl:for-each>
    </transactions>
    </xsl:template> 

      <xsl:template match="line_memo">
        <xsl:param name="newMemo"/>
            <new_memo><xsl:value-of select="concat(line_memo, '|', $newMemo)"/></new_memo>
    </xsl:template>
        <xsl:template match="amount">
        <xsl:param name="newAmount"/>
            <new_amount><xsl:value-of select="amount + $newAmount"/></new_amount>
    </xsl:template>
</xsl:stylesheet>

Desired Output:

<?xml version="1.0"?>
<transactions>
    <transaction>
        <name>Test Entry</name>
        <id>123</id>
        <line_memo>memo1|memo2|memo3</line_memo>
        <amount>30</amount>
    </transaction>
    <transaction>
        <name>Test 3</name>
        <id>1235</id>
        <line_memo>0123456789</line_memo>
        <amount>30</amount>
    </transaction>
</transactions>

My output using above style sheet:

    <?xml version="1.0" encoding="utf-16"?>
<transactions>
    <transaction>
        <name>Test Entry</name>
        <id>123</id>
        <line_memo>memo1</line_memo>
        <amount>5</amount>
    </transaction>
    <transaction>
        <new_memo>|memo2</new_memo>
        <new_amount>NaN</new_amount>
        <name>Test 1</name>
        <id>123</id>
    </transaction>
    <transaction>
        <new_memo>|memo3</new_memo>
        <new_memo>|memo3</new_memo>
        <new_amount>NaN</new_amount>
        <new_amount>NaN</new_amount>
        <name>Test 2</name>
        <id>123</id>
    </transaction>
    <transaction>
        <name>Test 3</name>
        <id>1235</id>
        <line_memo>0123456789</line_memo>
        <amount>30</amount>
    </transaction>
</transactions>

The final output having one element with the 123 id and adding up the amounts and concatenating the memo fields with a | delimiter.

In my style sheet, I add the apply-templates but I get an error, it says Required item type of @select attribute of xsl:apply-templates is node(); supplied value has item type xs:string.

How can I modify the preceding sibling elements and omit the current node?

Was it helpful?

Solution

Don't think of it in terms of "modifying" anything you've already output. What you're trying to do here is create one output transaction element for each group of input transaction elements that share the same ID. Since you're in XSLT 2.0 this is easy to do with for-each-group and the separator attribute on value-of:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes" />

  <xsl:template match="/*">
    <xsl:copy>
      <!-- process all input transactions with the same id as a group -->
      <xsl:for-each-group select="transaction" group-by="id">
        <transaction>
          <!-- copy name and id from the first instance -->
          <xsl:sequence select="name, id" />
          <!-- join all the memos in the group with | - if there's only one
               then there will be no separator -->
          <line_memo>
            <xsl:value-of select="current-group()/line_memo" separator="|" />
          </line_memo>
          <!-- sum the amounts -->
          <amount>
            <xsl:value-of select="sum(current-group()/amount)" />
          </amount>
        </transaction>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

When run on your sample input using Saxon 9 HE the required output is produced:

<?xml version="1.0" encoding="UTF-8"?>
<transactions>
   <transaction>
      <name>Test Entry</name>
      <id>123</id>
      <line_memo>memo1|memo2|memo3</line_memo>
      <amount>30</amount>
   </transaction>
   <transaction>
      <name>Test 3</name>
      <id>1235</id>
      <line_memo>0123456789</line_memo>
      <amount>30</amount>
   </transaction>
</transactions>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top