Sam Goldberg Sam Goldberg - 2 months ago 14
Java Question

JAXB: How to customize Xml serialization of double fields

I have a legacy class, with a lot of public double fields. All double fields are initialized with

Double.MAX_VALUE
to indicate that they are empty. (The legacy serialization is coded to ignore the field and not serialize if field is equals to
Double.MAX_VALUE
).

We are now trying to serialize this class to Xml using JAXB Marshaller. It is working fine, except that we want to prevent generating Xml for fields which equal
Double.MAX_VALUE
.

We aren't using a separate JAXB schema, just marking up our classes with various
javax.xml.bind.annotation
Annotations. If a schema is used, you can add a
<javaType>
element to specify a custom DataType converter. Is there any way to do this using Annotations or programmatically?

After trying approach recommended below, I still can't get
XmlAdapter
picked up:

@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value=EmptyDoubleValueHandler.class, type=Double.class), @XmlJavaTypeAdapter(value=EmptyDoubleValueHandler.class, type=double.class)})
package tta.penstock.data.iserver;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;


My top level class is: tta.penstock.data.iserver.OrderBlotter, which contains a list of tta.penstock.data.iserver.OrderResponseWrappers which extends
com.eztech.OrderResponse
. All the double fields are contained in
com.eztech.OrderResponse
.

My unit test code does the following:

JAXBContext context = JAXBContext.newInstance(new Class[] { OrderBlotter.class, OrderResponseWrapper.class, OrderResponse.class});

Marshaller marshaller = context.createMarshaller();
StringWriter stringWriter = new StringWriter();
marshaller.marshal(blotter, stringWriter);
System.out.println("result xml=\n" + stringWriter.toString());


But the double values still don't get handled by the
XmlAdapter
. I know I'm missing something basic, but I'm not sure what it is.

Answer

You could use an XmlAdapter:

The XmlAdapter

package example;

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

public class DoubleAdapter extends XmlAdapter<Double, Double>{

    @Override
    public Double unmarshal(Double v) throws Exception {
        return v;
    }

    @Override
    public Double marshal(Double v) throws Exception {
       if(Double.MAX_VALUE == v) {
           return null;
       } else {
           return v;
       }
    }

}

The Model Object

package example;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class Root {

    @XmlJavaTypeAdapter(DoubleAdapter.class)
    public Double maxDouble = Double.MAX_VALUE;

    @XmlJavaTypeAdapter(DoubleAdapter.class)
    public Double aDouble = 123d;

}

Demo Code

package example;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(new Root(), System.out);
    }

}

UPDATE

StaxMan's suggestion is a good one. If you specify the following package level annotation you can avoid the need of individually annotating all the Double properties

package-info.java

@XmlJavaTypeAdapters({
    @XmlJavaTypeAdapter(type=Double.class, value=DoubleAdapter.class)
})
package example;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
Comments