7

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.

vsminkov
  • 10,912
  • 2
  • 38
  • 50
Sam Goldberg
  • 6,711
  • 8
  • 52
  • 85

2 Answers2

15

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;
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • I was looking at that approach. The problem is that it would be problematic to mark up all the double fields in the legacy class. I was looking for a global "hook" where we could specify or put exactly the kind of type Adapter you list above. So i.e. something which says "for every double field in this class, use this XmlAdapter". – Sam Goldberg Nov 10 '10 at 22:07
  • 3
    You can also add @XmlAdapter annotations for packages (package-info.java can contain annotations!) -- check out java docs. That might make this approach feasible – StaxMan Nov 11 '10 at 04:05
  • I have been trying this approach, this seems like what is needed. But it seems that the XmlJavaTypeAdapter annotation I put in there isn't being picked up during serialization. I think it maybe because the fields are contained in a class defined in another package (outside of our project). But really, I'm not sure why it isn't being picked up. – Sam Goldberg Nov 11 '10 at 15:37
  • As long as those classes are included in your JAXBContext you should be fine. You also may want to check out EclipseLink JAXB (MOXy), I'm the tech lead: http://www.eclipse.org/eclipselink/moxy.php – bdoughan Nov 11 '10 at 15:40
  • I have tried the package notation approach, but the XmlAdapter is not being picked up. The classes involved: – Sam Goldberg Nov 12 '10 at 15:31
0

Write a getter that returns null, instead of Double.MAX_VALUE? (if type is 'double', need to change it to 'Double' first, to allow nulls). Since JAXB by default ignores writing out of nulls, that should achieve what you are trying to do. This assumes you can modify legacy class in question.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • 1
    Thanks for suggestion. Unfortunately, legacy class can't easily be modified (and there are a "million" public double fields which need to be wrapped). Was looking for an easier way out... – Sam Goldberg Nov 10 '10 at 22:09