I have an XML document where the number of decimal places a particular xs:decimal
should be reported in is 开发者_如何学JAVAheld in a sibling node. I'm currently struggling to find a simple way to output this via the format-number
function.
I can build a picture string with some other functions, but this seems terribly long-winded for what should be (at least imo) a relatively straightforward and common task.
e.g. what I'm currently doing is something like this:
<xsl:value-of
select="format-number(myNode/DecimalValue,
concat('#0.',
string-join(for $i in 1 to myNode/DecimalPlaces return '0'))"
/>
Is there a better way?
Very good question! That usually means, I don't know the answer but I hope someone else does as this is a pain for me too.
Anyway, i did some searching and I think the round-half-to-even
function might do the trick (http://www.xqueryfunctions.com/xq/fn_round-half-to-even.html)
Your code would become:
<xsl:value-of
select="
round-half-to-even(
myNode/DecimalValue
, myNode/DecimalPlaces
)
"
/>
Now off for a little tangent: For people that are using XSLT 1.1 or lower and XPath 1, you could use this:
<xsl:value-of
select="
concat(
substring-before(DecimalValue, '.')
, '.'
, substring(substring-after(DecimalValue, '.'), 1, DecimalPlaces -1)
, round(
concat(
substring(substring-after(DecimalValue, '.'), DecimalPlaces, 1)
, '.'
, substring(substring-after(DecimalValue, '.'), DecimalPlaces+1)
)
)
)
"
/>
Of course, this code is worse than the original, but if anyone knows how to solve the original question for XPath 1 and has a better idea than this, I'd love to hear that. (More and more often, I wish the world would have skipped XML altogether and moved immediately to JSON)
<!-- use a generous amount of zeros in a top-level variable -->
<xsl:variable name="zeros" select="'000000000000000000000000000000000'" />
<!-- …time passes… -->
<xsl:value-of select="
format-number(
myNode/DecimalValue,
concat('#0.', substring($zeros, 1, myNode/DecimalPlaces))
)
" />
You can abstract it away into a template:
<!-- template mode is merely to prevent collisions with other templates -->
<xsl:template match="myNode" mode="FormatValue">
<xsl:value-of select="
format-number(
DecimalValue,
concat('#0.', substring($zeros, 1, DecimalPlaces))
)
" />
</xsl:template>
<!-- call like this -->
<xsl:apply-templates select="myNode" mode="FormatValue" />
You can also make a named template and use the XSLT context node when calling it. Depends a bit on your input document and needs if this is feasible for you.
<xsl:template name="FormatValue">
<!-- same as above -->
</xsl:template>
<!-- call like this -->
<xsl:for-each select="myNode">
<xsl:call-template name="FormatValue" />
</xsl:for-each>
精彩评论