Hi,
 
I have found a minor incompatibility with XMLBeans and Java 1.5 when
creating documents with small numbers. XMLBeans uses BigDecimal for
xs:decimal elements. The problems is essentially that
BigDecimal.toString() now returns exponents (e.g. 1.1E-8) in certain
circumstances, particularly with small numbers. The 'E' character is not
valid in xs:decimal, so you end up with an invalid xml document. 
 
I use BigDecimal.setScale to ensure that my BigDecimal variables don't
exceed the precision limits set by my schema. I first noticed this
problem when setting the scale on a zero BigDecimal - new
BigDecimal(0).setScale(9).toString(), yields "0E-9". The below example
shows the problem using a test schema:

<?xml version="1.0" encoding="UTF-8" ?> <xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"; xmlns="test"
targetNamespace="test">
        <xs:element name="test" type="Test"/>
        <xs:complexType name="Test">
                <xs:annotation/>
                <xs:sequence>
                        <xs:element name="test" type="TestNo"
minOccurs="0" maxOccurs="unbounded"/>
                </xs:sequence>
        </xs:complexType>
        <xs:simpleType name="TestNo">
                <xs:restriction base="xs:decimal">
                        <xs:fractionDigits value="9" /> 
                        <xs:totalDigits value="18" /> 
                </xs:restriction>
        </xs:simpleType>
</xs:schema>

My test program is:
________________________________________________________________________
__
public static void main(String[] args)
{
    TestDocument doc = TestDocument.Factory.newInstance();
    Test test = doc.addNewTest();
    test.addTest(new BigDecimal(1.2345678923456789).setScale(9
,BigDecimal.ROUND_HALF_UP)));
    test.addTest(new BigDecimal(0.0000000111).setScale(9
,BigDecimal.ROUND_HALF_UP)));
    test.addTest(new BigDecimal(0).setScale(9
,BigDecimal.ROUND_HALF_UP)));
    test.addTest(new BigDecimal(0.000001));
    test.addTest(new BigDecimal(0));
    
    ArrayList validationErrors = new ArrayList();
    XmlOptions validationOptions = (new
XmlOptions()).setErrorListener(validationErrors);
    if (!doc.validate(validationOptions)) {
        Iterator iter = validationErrors.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());
        }
    }
    System.out.println(doc.toString());
}
________________________________________________________________________
__

This prints out:
________________________________________________________________________
__
error: decimal: Invalid decimal value: unexpected char '69'
error: decimal: Invalid decimal value: unexpected char '69'
error: decimal: Invalid decimal value: unexpected char '69'
<test xmlns="test">
  <test xmlns="">1.234567892</test>
  <test xmlns="">1.1E-8</test>
  <test xmlns="">0E-9</test>
  <test
xmlns="">9.999999999999999547481118258862586856139387236908078193664E-7<
/test>
  <test xmlns="">0</test>
</test>
 
________________________________________________________________________
__

The long term solution, I guess, is to use BigDecimal.toPlainString()
when creating an xml document within the xml beans code
(JavaDecimalHolder.compute_text). However, this would make xmlbeans
incompatible with Java 1.4 which I'm sure is not possible. 

In the short term (i.e. with current xmlBeans), as far as I can see
there are two ways around this. 
1.      Use XMLBeans' generated types to force the string value.
However, this rather bloats code as each 'set' becomes 3 lines. For
example:

interest.setAmt(bigDecimal.setScale(3,BigDecimal.ROUND_HALF_UP));
becomes:
AmountOfMoney money = AmountOfMoney.Factory.newInstance();
money.setStringValue(bigDecimal.setScale(3,BigDecimal.ROUND_HALF_UP).toP
lainString());
interest.xsetAmt(money);

2.      Create a new class that inherits from BigDecimal who's toString
calls toPlainString, to restore the Java 1.4 behaviour, as below:

public class PlainBigDecimal extends BigDecimal {
    public PlainBigDecimal(BigDecimal bd)
    {
        super(bd.unscaledValue(),bd.scale());
    }
    
    public String toString()
    {
        return super.toPlainString();
    }
}

With this our example becomes:

...
test.addTest(new PlainBigDecimal(new
BigDecimal(0.0000000111).setScale(9,BigDecimal.ROUND_HALF_UP)));
test.addTest(new PlainBigDecimal(new
BigDecimal(0).setScale(9,BigDecimal.ROUND_HALF_UP)));
...

Which prints:

<test xmlns="test">
  <test xmlns="">0.000000011</test>
  <test xmlns="">0.000000000</test>
</test>

Please let me know if you can think of any better solutions.

Kind regards,
Ian Jones

Visit our website at http://www.ubs.com

This message contains confidential information and is intended only
for the individual named.  If you are not the named addressee you
should not disseminate, distribute or copy this e-mail.  Please
notify the sender immediately by e-mail if you have received this
e-mail by mistake and delete this e-mail from your system.

E-mail transmission cannot be guaranteed to be secure or error-free
as information could be intercepted, corrupted, lost, destroyed,
arrive late or incomplete, or contain viruses.  The sender therefore
does not accept liability for any errors or omissions in the contents
of this message which arise as a result of e-mail transmission.  If
verification is required please request a hard-copy version.  This
message is provided for informational purposes and should not be
construed as a solicitation or offer to buy or sell any securities or
related financial instruments.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to