2

I need to sort only the <Transaction-Detail> nodes (by the <tran-id> child node) of the following file:

<TransActDO clear="true" removed="false">
  <stmt-reason-code>1001</stmt-reason-code>
  <Transaction-Detail clear="true" removed="false">
    <txn-amt>788.20</txn-amt>
    <txn-description>New Purchase</txn-description>
    <tran-id>3271</tran-id>
  </Transaction-Detail>
  <Transaction-Detail clear="true" removed="false">
    <txn-amt>-68.20</txn-amt>
    <txn-description>Return</txn-description>
    <tran-id>27795</tran-id>
  </Transaction-Detail>
  <Transaction-Detail clear="true" removed="false">
    <txn-amt>0.00</txn-amt>
    <txn-description>Comment</txn-description>
    <transaction-reason-desc>No Reason</transaction-reason-desc>
    <tran-id>13365</tran-id>
    <transaction-reason-code>0</transaction-reason-code>
  </Transaction-Detail>
  <Transaction-Detail clear="true" removed="false">
    <txn-amt>343.45</txn-amt>
    <txn-description>New Purchase</txn-description>
    <tran-id>7558</tran-id>
  </Transaction-Detail>
  <Transaction-Detail clear="true" removed="false">
    <txn-amt>0.00</txn-amt>
    <txn-description>Comment</txn-description>
    <transaction-reason-desc>No Reason</transaction-reason-desc>
    <tran-id>6512</tran-id>
    <transaction-reason-code>0</transaction-reason-code>
  </Transaction-Detail>
  <account-no>123456789</account-no>
  <payer-name>JOHN DOE</payer-name>
  <Product-Detail clear="true" removed="false">
    <Name>WIDGET</Name>
    <Amount>89.00</Amount>
  </Product-Detail>
  <Product-Detail clear="true" removed="false">
    <Name>NEWER WIDGET</Name>
    <Amount>99.99</Amount>
  </Product-Detail>
  <stmt-reason-desc>Web Statement</stmt-reason-desc>
  <type>Original</type>
</TransActDO>

The output is XML and needs to also copy all other nodes and attributes from the original file. In essence, copy everything, just sort the Transaction-Detail nodes.

I've got this far:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

  <xsl:template match="TransActDO">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="Transaction-Detail">
        <xsl:sort select="tran-id" data-type="number" order="ascending"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

This results in a properly sorted XML file containing only the Transaction-Detail nodes and their child nodes. When ever I attempt to add additional logic to copy the rest of the nodes, the sort breaks.

I guess I'm having difficulty wrapping my brain around XSLT execution theory and syntax.

Any help is much appreciated!
-nth-

1
  • You need the nodes that preceed the set of Transaction-Detail nodes to remain at the top and those that follow to remain at the bottom or cant the Transaction-Detail nodes be moved to the bottom of the XMl? (BTW I'm assuming for legacy reasons you can't just re-design the XML so that things like Product-Detail and Transaction-Detail each have a containing parent node so they don't have siblings with different tagnames) Commented Jan 13, 2010 at 22:10

1 Answer 1

4

This will do it:-

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="Windows-1252"  />

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

    <xsl:template match="TransActDO">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[not(preceding-sibling::Transaction-Detail) and not(self::Transaction-Detail)]"/>
            <xsl:apply-templates select="Transaction-Detail">
                <xsl:sort select="tran-id" data-type="number" order="ascending"/>
            </xsl:apply-templates>
            <xsl:apply-templates select="@*|node()[not(following-sibling::Transaction-Detail) and not(self::Transaction-Detail)]"/>
    </xsl:copy>
    </xsl:template>


</xsl:stylesheet>

If you don't mind moving the Transaction-Detail elements to either the top or bottom of the TransActDO element you could simplify the inner set of apply templates to:-

            <xsl:apply-templates select="@*|node()[not(self::Transaction-Detail)]"/>
            <xsl:apply-templates select="Transaction-Detail">
                <xsl:sort select="tran-id" data-type="number" order="ascending"/>
            </xsl:apply-templates>
Sign up to request clarification or add additional context in comments.

2 Comments

+1. I've just noticed that you've posted the exact same code I just posted (guess I should have read your second code sample…). Deleting my answer to avoid redundancy.
Thanks Anthony. It did indeed work! Unfortunately I don't control over the creation of the xml structure but this,at least, will solve the sorting issue.

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.