Subtract in xslt 1.0

2019-07-22 09:31发布

问题:

I have problem with subtract in this example file:

<EVENTS>
<ROW ID="204" ID_PLACE="1" EVENT_TYPE="B" EVENT_NAME="TEST1" EVENT_ID="201">
<PRICE>
<ROW EVENT_PRICE="165,00"/>
</PRICE>
</ROW>
<ROW ID="205" ID_PLACE="1" EVENT_TYPE="P" EVENT_NAME="TEST1" EVENT_ID="201">
<PRICE>
<ROW EVENT_PRICE="125,00"/>
</PRICE>
</ROW>
</EVENTS>

Her is a relevant piece of my XSLT:

<xsl:for-each select="EVENTS/ROW/PRICE/ROW">
  <xsl:variable name="event_b">
    <xsl:choose>
      <xsl:when test="EVENT_TYPE=B">
        <xsl:value-of select="EVENT_PRICE" />
      </xsl:when>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="event_p">
    <xsl:choose>
      <xsl:when test="EVENT_TYPE=P">
        <xsl:value-of select="EVENT_PRICE" />
      </xsl:when>
    </xsl:choose>
  </xsl:variable>
  <xsl:value-of select="number($event_b) - number($event_p)" />
</xsl:for-each>

I have to subtract event_price with type P from the corresponding one with type B. In this example I want to get a result 40, and output it into the result tree, but it isn't working. What's wrong?

回答1:

You are trying to convert strings to numbers, but those strings are not formatted correctly for XSL / XPath / XSI numbers. Specifically, the number() constructor recognizes only the period ('.') character as a decimal separator, but your input strings seem to use commas (',') in that role.

If you cannot correct the data to follow prevailing convention for representing decimal numbers, then you'll need to account for the variant convention in your stylesheet. You can do this with the translate function.

Before you even get there, however, you have severe problems in your stylesheet structure, among them:

  • your outer xsl:for-each does not make sense. You are iterating over individual (inner) <ROW>s, but you intend to handle pairs of corresponding rows. A convenient context for that would be the closest common ancestor, though there are other ways to approach the problem.

  • Your select and test expressions are attempting to refer to child elements where you mean to refer to attributes.

  • In some places, your tests are comparing to (non-existent) child elements instead of string values.

Overall, you've made it much harder than it needs to be. For the input you've presented, you might do something like this:

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

  <xsl:template match="EVENTS">
    <!-- Note @attribute_name syntax for referring to attributes -->
    <!-- Note quoted form for string literals -->
    <!-- Note that these expressions are evaluated relative to the EVENTS element
         matched by this template -->
    <!-- Note no iteration: each value wanted is identified via an
         appropriate XPath expression -->
    <!-- Note use of a select expression to specify the variables' values.
         That doesn't make much difference in this particular case, but it
         _is_ significant. -->
    <xsl:variable name="event_b"
        select="ROW[@EVENT_TYPE = 'B']/PRICE/ROW/@EVENT_PRICE"/>
    <xsl:variable name="event_p"
        select="ROW[@EVENT_TYPE = 'P']/PRICE/ROW/@EVENT_PRICE"/>

    <!-- Note use of translate() to translate commas to periods before converting
         to numbers -->
    <xsl:value-of select="number(translate($event_b, ',', '.')) - number(translate($event_p, ',', '.'))" />
  </xsl:template>

</xsl:stylesheet>


标签: xml xslt