I have a problem currently where a system we are connecting to expects to receive XML which contains, among other things, three double fields formatted to one decimal place. Personally I feel that our system should just be able to send values in default format and then it's up to other systems to format their own representation as they please, but alas this doesn't seem to be an option.
My Java-based system is currently converting objects to XML through the use of XStream. We have an XSD which accompanies the XML and defines the various elements as string, double, dateTime, etc.
I have three double fields which hold values like 12.5, 100.123, 5.23445 etc. Right now they are converted pretty much as-is into the XML. What I need is these values to be formatted in the XML to one decimal place; 12.5, 100.1, 5.2, etc.
I have briefly thought up options to accomplish this:
I'd to pick your collective brains as to what would be the 'accepted' way / best practice to use in a situation like this.
Thanks, Dave.
XStream has converters (tutorial). You would have to register your own Double converter that will handle this. In the converter use DecimalFormat to limit the number of decimal places.
This can be done in a single XPath expression.
Use:
floor(.) + round(10*(. -floor(.))) div 10
Verification using XSLT as a host of XPath:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[contains(.,'.')]">
<xsl:value-of select=
"floor(.) + round(10*(. -floor(.))) div 10"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<t>
<n>12.5</n>
<n>100.123</n>
<n>5.26445</n>
</t>
the wanted, correct result is produced:
<t>
<n>12.5</n>
<n>100.1</n>
<n>5.3</n>
</t>
Explanation: Use of the standard XPath functions floor(), round() and the XPath operator div and your logic.
Generalized expression:
floor(.) + round($vFactor*(. -floor(.))) div $vFactor
where $vFactor is 10^N, where N is the number of digits after the decimal point we want.
Using this expression, the modified XSLT transformation is this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pPrecision" select="4"/>
<xsl:variable name="vFactor" select=
"substring('10000000000000000000000',
1, $pPrecision+1
)
"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[contains(.,'.')]">
<xsl:value-of select=
"floor(.) + round($vFactor*(. -floor(.))) div $vFactor"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the same XML document (above), we produce the wanted output for any meaningful value of $pPrecision. In the above example it is set to 4 and the result contains all numbers rounded up to four digits after the decimal point:
<t>
<n>12.5</n>
<n>100.123</n>
<n>5.2645</n>
</t>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With