JAXB marshalling for BigDecimal using fractionDigits
Asked Answered
R

4

6

So here's my problem. I'm given an XSD to which my generated XML file should comply. Using the org.apache.cxf.cxf-xjc-plugin maven plugin and an external binding file I generate the source code. But when I'm trying marshall my object the generated XML doesn't meet my requirements.

My XSD contains the following:

<xsd:element maxOccurs="1" minOccurs="0" name="amount">
  <xsd:simpleType>
    <xsd:restriction base="xsd:decimal">
      <xsd:totalDigits value="13" />
      <xsd:fractionDigits value="2" />
    </xsd:restriction>
  </xsd:simpleType>
</xsd:element>
...
<xsd:element maxOccurs="1" minOccurs="0" name="rate">
  <xsd:simpleType>
    <xsd:restriction base="xsd:decimal">
      <xsd:totalDigits value="8" />
      <xsd:fractionDigits value="5" />
    </xsd:restriction>
  </xsd:simpleType>
</xsd:element>

And the generated piece of XML looks like this:

<amount>109.5</amount>
...
<rate>10.25</rate>

While I was expecting it to be:

<amount>109.50</amount>
...
<rate>10.25000</rate>

Is there a way to solve this problem in a clean way?

I would prefer not writing several adapters for every single totalDigits, fractionDigits combination. And as the XSD is subject to change I'd like to leave the generated source code untouched.

Ready answered 13/2, 2013 at 7:51 Comment(3)
The w3c document on the decimal datatype says fractionDigits specifies the upper limit on the number of digits after the decimal separator. It should not matter to anything reading the data if the right-hand zeros are absent. What is the use case that requires the zero padding?Signorelli
@Jim The (legacy) application parsing my generated XML file requires the zero padding ;-)Ready
Can you use precisionDecimal as in my answer?Signorelli
H
6

You will need to use XmlAdapter for this use case. Below is a sample binding file that will help you generate them. The logic would be contained in a DecimalFormatter class that contained methods for all the different required formats.

<jxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb" version="2.1">
    <jxb:bindings schemaLocation="schema.xsd">
        <jxb:bindings node="//xs:element[@name='amount']">
            <jxb:property>
                <jxb:baseType>
                    <jxb:javaType name="java.math.BigDecimal"
                        parseMethod="org.example.DecimalFormatter.parseDecimal"
                        printMethod="org.example.DecimalFormatter.printDecimal_2Places" />
                </jxb:baseType>
            </jxb:property>
        </jxb:bindings>
        <jxb:bindings node="//xs:element[@name='rate']">
            <jxb:property>
                <jxb:baseType>
                    <jxb:javaType name="java.math.BigDecimal"
                        parseMethod="org.example.DecimalFormatter.parseDecimal"
                        printMethod="org.example.DecimalFormatter.printDecimal_5Places" />
                </jxb:baseType>
            </jxb:property>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

For More Information

Hutcherson answered 13/2, 2013 at 10:47 Comment(1)
I'm actually doing this right now, but I'm trying to find a cleaner solution since this requires me to create a printMethod for every totalDigits - fractionDigits combination.Ready
M
1

I've created some special XmlAdapter, for example

public class BigDecimal8PlaceAdapter extends XmlAdapter<String, BigDecimal> {

@Override
public String marshal(BigDecimal v) throws Exception {
  DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols();
  otherSymbols.setDecimalSeparator('.');
  DecimalFormat df = new DecimalFormat("#0.00000000",otherSymbols);
  return df.format(v);
}

@Override
public BigDecimal unmarshal(String v) throws Exception {
    Double d = Double.valueOf(v);
    return BigDecimal.valueOf(d);
}

}

then i add the XmlAdapter in the properties:

@XmlElement(name = "rate", required = true)
@XmlJavaTypeAdapter(BigDecimal8PlaceAdapter.class)
protected BigDecimal rate;

Then, do the same thing with 2 decimals adapeter.

Multifarious answered 28/1, 2019 at 11:45 Comment(0)
S
0

If you can change the XSD to use the precisionDecimal type documented here you might be able to use the minScale and maxScale facets set to the same value.

Signorelli answered 13/2, 2013 at 8:20 Comment(1)
According to this answer, the JAXB spec does not cover converting XML Schema 1.1 to Java. And XJC throws the following exception [ERROR] src-resolve: Cannot resolve the name 'xsd:precisionDecimal' to a(n) 'type definition' component. when I try it.Ready
T
0

I'm confused as to what could possibly be gained by maintaining the trailing zeros.

If the trailing zeros are that important to preserve, then use a string value instead of a number and use attributes to specify its width and decimals.

In any case, the trailing zeros will never impact calculations using any of these values, and the only way you would be able to present them is by converting the results into a string and padding it yourself. For some help doing that, you may want to see...

CXF client SOAP message formatting

Turkestan answered 22/1, 2014 at 22:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.