Re: [xsl] Help with staregies for outputting an incremental number in xslt

Subject: Re: [xsl] Help with staregies for outputting an incremental number in xslt
From: Adam Lipscombe <adam.lipscombe@xxxxxxxxxx>
Date: Fri, 30 Nov 2007 09:32:22 +0000
Scott, James, Michael,



As you may have guessed I am primarily a Java guy who dips into xslt when I have to.
I will have to go on a course before doing much more I suspect :-)

Many many thanks for your massive help.



Adam


Scott Trenda wrote:
Adam,

If <xsl:variable> has any content, such as the <xsl:value-of/> in the
first instance below, then its result becomes what's known as a "result
tree fragment" in XSLT 1.0, and essentially becomes its own document
containing its own node-set in XSLT 2.0. However, if you leave
<xsl:variable/> empty and use the select attribute, then the variable
simply becomes a short-hand pointer to the node-set that the XPath
returns. As you could guess, the latter situation is much faster for the
processor to deal with.

Also, I was looking over the stylesheet you provided in your first
e-mail... good god, $includeLine looks convoluted. See if you can reduce
that to a single XPath expression! All you're selecting in each of those
cases is true() or false(), which is the exactly the same as the value
of the test condition in the <xsl:if> element to start with! Also, cut
down on frivolous variables when you can. If you're only using the
variable once, there's very few reasons why you shouldn't refactor it
out. Take a look at this:

Here was your initial stylesheet:
  <xsl:variable name="description"
select="../../expensys:ItemDescription"/>
  <!-- Node ommission
        Set includeLine=false for personal spend lines with CorpCardRec
personal settlement
        as we dont want to output these -->
  <xsl:variable name="includeLine">
    <xsl:choose>
      <xsl:when test="$description = 'Personal Spend'">
        <xsl:if test="$transactionType = 'CorporateCardReconciliation'">
          <xsl:if test="$corporateCardSettlementType = 'corporate'">
            <xsl:value-of select="true()"/>
          </xsl:if>
          <xsl:if test="$corporateCardSettlementType = 'personal'">
            <xsl:value-of select="false()"/>
          </xsl:if>
        </xsl:if>
        <xsl:if test="$transactionType !=
'CorporateCardReconciliation'">
          <xsl:value-of select="true()"/>
        </xsl:if>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="true()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:if test="$includeLine='true'">
    <txn:Line>
      <txn:Number>
        <xsl:value-of select="position()"/>
      </txn:Number>
      <!-- More data from node is output here..... -->
    </txn:Line>
  </xsl:if>

The entire variable set can be factored down to this (and probably
further; you didn't specify what $transactionType or
$corporateCardSettlementType were here):
  <!-- Omit personal spend lines with CorpCardRec personal settlement
-->
  <xsl:if test="not($transactionType = 'CorporateCardReconciliation'
                and $corporateCardSettlementType = 'personal'
                and ../../expensys:ItemDescription = 'Personal Spend')">
    <txn:Line>
      <txn:Number>
        <xsl:value-of select="position()"/>
      </txn:Number>
      <!-- More data from node is output here..... -->
    </txn:Line>
  </xsl:if>

Start using better XPath expressions, and stop relying so heavily on
temporary XSLT variables. Your transformations will run faster, you'll
be able to do complicated sorting and key selections, and most
importantly, you (and other developers) will be willing to touch the
stylesheet later.



Regarding the numbering, I think you'll want to use the <xsl:number>
element in the end. It offers the most control for counting the position
of nodes in their document, regardless of their position in the context
node-set. I believe it's somewhat slower than position(), due to its
nature of counting the preceding:: and preceding-sibling:: axes, but I
don't think it'll make a noticeable difference in your stylesheet here.

From the constraints you included in your last e-mail, I think it'd go
something like this...

<xsl:template match="Transaction [@transactionType =
'CorporateCardReconciliation' and Header/CorporateCardSettlementType =
'personal']/Lines/Line">
  <txn:Number>
    <xsl:number count="Line[not(ItemDescription = 'Personal Spend')]"/>
  </txn:Number>
  <!-- the rest of your processing here... -->
</xsl:template>


Good luck with the rest of your project here!


~ Scott


-----Original Message-----
From: Adam Lipscombe [mailto:adam.lipscombe@xxxxxxxxxx] Sent: Thursday, November 29, 2007 10:46 AM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re: [xsl] Help with staregies for outputting an incremental
number in xslt


Michael

Thanks for that.  What I mean by omitted nodes is that my transform does
not output some of the nodes.


> Incidentally, please don't do this: > >> <xsl:variable name="description"> >> <xsl:value-of select="../../expensys:ItemDescription"/> >> </xsl:variable> > > when you mean this: > >> <xsl:variable name="description" > select="../../expensys:ItemDescription"/>

Point taken - thanks. I do n ot understand the difference between these
constructs. Could you advise?




Here is a snippet of the input xml file:


<Transaction transactionType="CorporateCardReconciliation">
<Header>
<TransactionIdentifier><![CDATA[CC-DD101-S1]]></TransactionIdentifier>
<EnterpriseLevel><![CDATA[ACME Engineering]]></EnterpriseLevel>
<ClaimantName><![CDATA[Dolores Delegate]]></ClaimantName>
<ClaimantIdentifier><![CDATA[DD101]]></ClaimantIdentifier>
<Description><![CDATA[BarclayCard Statement:
01/02/2007]]></Description>
<Currency><![CDATA[GBP]]></Currency>
<CreationDate><![CDATA[2007-11-29T00:00:00+00:00]]></CreationDate>
<ApprovedDate><![CDATA[2007-11-29T00:00:00+00:00]]></ApprovedDate>
<ProcessedDate><![CDATA[2007-11-29T00:00:00+00:00]]></ProcessedDate>
<TransactionAmount>
<value><![CDATA[20.00]]></value>
<sense><![CDATA[credit]]></sense>
</TransactionAmount>
<Approvers>
<Approver><![CDATA[Eddie Executive]]></Approver>
</Approvers>
<StatementDate><![CDATA[2007-02-01T00:00:00+00:00]]></StatementDate>
<StatementAmount><![CDATA[35.00]]></StatementAmount>
<CorporateCardSettlementType><![CDATA[personal]]></CorporateCardSettleme
ntType>
</Header>
<Lines>
<Line>
<ItemDescription><![CDATA[Personal Spend]]></ItemDescription>
<ItemNumber><![CDATA[1]]></ItemNumber>
<SpendDate><![CDATA[2007-01-08T00:00:00+00:00]]></SpendDate>
<CostCentre>
<costCentreCode><![CDATA[CC1]]></costCentreCode>
</CostCentre>
<ProjectCode><![CDATA[CODE01]]></ProjectCode>
<Accruals>
<Accrual>
<accountCode><![CDATA[999998]]></accountCode>
<amount>
<value><![CDATA[15.00]]></value>
<sense><![CDATA[debit]]></sense>
</amount>
</Accrual>
</Accruals>
</Line>
<Line>
<ItemDescription><![CDATA[test dol]]></ItemDescription>
<ItemNumber><![CDATA[2]]></ItemNumber>
<SpendDate><![CDATA[2007-01-08T00:00:00+00:00]]></SpendDate>
<CostCentre>
<costCentreCode><![CDATA[CC1]]></costCentreCode>
</CostCentre>
<ProjectCode><![CDATA[CODE01]]></ProjectCode>
<Accruals>
<Accrual>
<accountCode><![CDATA[100102]]></accountCode>
<amount>
<value><![CDATA[20.00]]></value>
<sense><![CDATA[debit]]></sense>
</amount>
</Accrual>
</Accruals>
</Line>
</Lines>
</Transaction>




The first line above has the description "Personal Spend". I dont want
to output this line in the transform, if (and only if) the
transactionType="CorporateCardReconciliation" AND the CorporateCardSettlementType="personal".


In this scenario the first line should the Line that has description =
"test dol", however its txn:Number shoudl be "1", not "2".
In all other circumstances the "Personal Spend" line should be output as
normal.




Any ideas?


TIA - Adam







Michael Kay wrote:
I don't understand what you mean by "omitted nodes" (I have trouble
discussing non-existent objects). If position() counts it, then it
must be
there. Please show us your XML to illustrate what you are talking
about.
Incidentally, please don't do this:

   <xsl:variable name="description">
     <xsl:value-of select="../../expensys:ItemDescription"/>
   </xsl:variable>
when you mean this:

<xsl:variable name="description"
select="../../expensys:ItemDescription"/>

It's the kind of thing that gives XSLT an undeserved reputation for
being
verbose and slow.

Michael Kay
http://www.saxonica.com/





-- Adam Lipscombe

T: 01872 575083
M: 07957 548686
E: adam.lipscombe@xxxxxxxxxx

Current Thread