Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jaxb:global binding Double Type Conversion Using XMLAdapter

Tags:

java

jaxb

xsd

I have an element defined as an xsd:double. If I try and place a value say 285 into the element and then I marshal it I get an output of 285.0... That is fine .However, if I put a value in of say 285292746, I get an output of 2.85292746E8 when I marshal. I like something so that double output does not get converted into scientific notation with decimal? Basically I want 285292746 or 2852292746.0000000

java.lang.Double.toString() uses "computerized scientific notation" for certain values which produce isues for xml.

I know double representation of given value is fine. But the fact the value was in exponential format, The system that I am working on is accept my XML but does not know what to do with the exponential value and that causes my program not to work correctly. Changing xsd:double type in WSDL or server is not feasible for me. I am working on client side.

I came across Jaxb:binding for xsd:double type. I am still having hard time to fix the issue to send values double value in non –exponents format.

package com.logger.client 
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.DatatypeConverter;

public class JaxbDoubleSerializer  extends XmlAdapter<String, Double>
{


    public Double unmarshal(String value) {
        return ((double)javax.xml.bind.DatatypeConverter.parseDouble(value));
    }

    public String marshal(Double value) {
        if (value == null) {
            return null;
        }
        return (javax.xml.bind.DatatypeConverter.printDouble((double)(double)value));
    }

}

I need help how to use DoubleSerializer so that I can pass values without exponents. I tried using xmlAdapter Annotation in my class MyLogClient.java. I need to know how I can solve this situation.

package com.logger.client 
import javax.xml.ws.BindingProvider;
import javax.xml.bind.JAXBElement;import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;


public class MyLogClient 
{
   //Private member fields

  /** Object factory used to create user-and-role specific objects. */
    private static final ObjectFactory _of = new ObjectFactory();

     @XmlJavaTypeAdapter(JaxbDoubleSerializer.class)
   public JAXBElement<Double> msgFileId;

       @XmlJavaTypeAdapter(JaxbDoubleSerializer.class)
   public Double dNumber;


public final void createEntry;
    (
        final String userName,
        final String time,
        final String logMsgStringId,
        final Params logMsgParamsVal,
        final Integer logMessageFieldID
    )

    throws JAXBException
    {

       JAXBElement<String> username = _of.createParamsParam(userName);
      JAXBElement<String> strTime       = _of.createLogRequestTime(time);


        // Build the LogRequest request.
       final LogRequest _LogRequest = _of.createLogRequest();
       _LogRequest.setUserName(userName);
       _LogRequest.setTime(strTime);

        //Following is the main problem
        int nMsgArgs =  285292746;
       dNumber = Double.parseDouble(Integer.toString(nMsgArgs));  
      //After parsing double Value I get dNumber is now  2.85292746E8

      //MsgFile Id is of Type JAXBElement<Double>
      //CreateLogMessageIdentifier takes Double param
      //So the main problem is here..the value of double field in soap request 
      //is being sent in exponential format. I need to send as I explained above
      //285292746.

       msgFileId   = _of.createLogMessageIdentifier(dNumber); 

      JAXBElement<String> strIdVal = _of.createLogMessageFileId(logMsgStringId);

      final LogMessage logMessage = _of.createLogMessage();

     JAXBElement<Params> _logMsgParams =_of.createLogMessageParams(logMsgParamsVal);

     //Following is where I am trying to use marshall for double type.

     JAXBContext context = JAXBContext.newInstance("package com.logger.client ");
     context.createMarshaller().marshal(msgFileId, System.out);

      logMessage.setIdentifier(msgFileId);  //Method takes JAXBElement<Double>

      logMessage.setFileId(strIdVal );
      logMessage.setParams(_logMsgParams);

      JAXBElement<LogMessage> logMsgValue = _of.createLogRequestLogMessage(logMessage);


        _LogRequest.setLogMessage(logMsgValue);

        // Set the log entry
        port.log(_LogRequest); //Request is sent to server.

}

WSDL xsd:type declaration is below:-

  <xsd:complexType name="LogMessage">
         <xsd:sequence>
        <xsd:element name="fileId" type="xsd:string" minOccurs="0" nillable="true" />
        <xsd:element name="identifier" type="xsd:double" minOccurs="0" nillable="true" />
        <xsd:element name="params" type="tns:Params" minOccurs="0" nillable="true" />
         </xsd:sequence>
           </xsd:complexType>

Output for identifier field is:-

     <identifier> 2.85292746E8</indentifier> 

Whereas I want to send as. Because system does accept/recognize following types.

<identifier> 285292746</indentifier> 
or 
<identifier> 285292746.00000000</indentifier> 
like image 417
user1029083 Avatar asked Nov 04 '11 05:11

user1029083


2 Answers

You could use an external bindings file like the following:

binding.xml

<jxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">

    <jxb:globalBindings>
            <jxb:javaType name="java.lang.Double"
                xmlType="xs:double"
                parseMethod="javax.xml.bind.DatatypeConverter.parseDouble"
                printMethod="javax.xml.bind.DatatypeConverter.printDouble" />
    </jxb:globalBindings>

</jxb:bindings>

root.xsd

<?xml version="1.0"?>
<xs:schema 
    elementFormDefault="qualified"
    targetNamespace="http://www.example.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:complexType name="root">
        <xs:sequence>
            <xs:element name="foo" type="xs:double" />
            <xs:element name="bar" type="xs:double" />
        </xs:sequence>
    </xs:complexType>

</xs:schema>

XJC Call

xjc -d out -b binding.xml root.xsd

Root

The class generated for the root type will have XmlAdapter classes registered on the Double properties:

package com.example;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.w3._2001.xmlschema.Adapter1;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "root", propOrder = {
    "foo",
    "bar"
})
public class Root {

    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(Adapter1 .class)
    @XmlSchemaType(name = "double")
    protected Double foo;
    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(Adapter1 .class)
    @XmlSchemaType(name = "double")
    protected Double bar;

    public Double getFoo() {
        return foo;
    }

    public void setFoo(Double value) {
        this.foo = value;
    }

    public Double getBar() {
        return bar;
    }

    public void setBar(Double value) {
        this.bar = value;
    }

}

Adapter1

The XmlAdapter utilizes the methods you configured in the binding.xml file.

package org.w3._2001.xmlschema;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class Adapter1 extends XmlAdapter<String, Double> {

    public Double unmarshal(String value) {
        return (javax.xml.bind.DatatypeConverter.parseDouble(value));
    }

    public String marshal(Double value) {
        if (value == null) {
            return null;
        }
        return (javax.xml.bind.DatatypeConverter.printDouble(value));
    }

}
like image 93
bdoughan Avatar answered Nov 16 '22 01:11

bdoughan


The best way might be to use a custom binding (javaType clause) with your JAXB compiler. Documentation can be found here. Depending if you have to marshal and unmarshall doubles, the solution might be as simple as overriding the mapping to something such as a BigDecimal at the global level (with the understanding that although doubles and decimals are totally different things, you seem to want a decimal representation), or to use a datatype converter (the link above gives you examples for all these options).

like image 30
Petru Gardea Avatar answered Nov 15 '22 23:11

Petru Gardea