The Java – JAXB XML adapter works through annotations, but not through setadapter
I know all about how to use xmladapters to convert unmappable types, or just change how some objects are serialized / deserialized into XML If I use annotations (package level or otherwise), everything will be fine The problem is that I'm trying to change the representation of third-party objects. I can't change the source code to (that is, to inject comments)
This should not be a problem, considering that the Marshall object has a method manually adding adapters Unfortunately, no matter what I do, I can't get set to "kick" in this way For example, I have a class that represents a point (geocentric coordinates) in XYZ space In the XML I produce, I want to convert it to lat / long / altitude (geodetic coordinates) This is my course:
Geocentric
package testJaxb; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class GeocentricCoordinate { // Units are in meters; see http://en.wikipedia.org/wiki/Geocentric_coordinates private double x; private double y; private double z; @XmlAttribute public double getX() { return x; } public void setX(double x) { this.x = x; } @XmlAttribute public double getY() { return y; } public void setY(double y) { this.y = y; } @XmlAttribute public double getZ() { return z; } public void setZ(double z) { this.z = z; } }
earth
package testJaxb; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; /** * @see http://en.wikipedia.org/wiki/Geodetic_system */ @XmlRootElement public class GeodeticCoordinate { private double latitude; private double longitude; // Meters private double altitude; public GeodeticCoordinate() { this(0,0); } public GeodeticCoordinate(double latitude,double longitude,double altitude) { super(); this.latitude = latitude; this.longitude = longitude; this.altitude = altitude; } @XmlAttribute public double getLatitude() { return latitude; } public void setLatitude(double latitude) { this.latitude = latitude; } @XmlAttribute public double getLongitude() { return longitude; } public void setLongitude(double longitude) { this.longitude = longitude; } @XmlAttribute public double getAltitude() { return altitude; } public void setAltitude(double altitude) { this.altitude = altitude; } }
GeocentricToGeodeticLocationAdapter
package testJaxb; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.adapters.XmlAdapter; /** * One of our systems uses xyz coordinates to represent locations. Consumers of our XML would much * prefer lat/lon/altitude. This handles converting between xyz and lat lon alt. * * @author ndunn * */ public class GeocentricToGeodeticLocationAdapter extends XmlAdapter<GeodeticCoordinate,GeocentricCoordinate> { @Override public GeodeticCoordinate marshal(GeocentricCoordinate arg0) throws Exception { // TODO: do a real coordinate transformation GeodeticCoordinate coordinate = new GeodeticCoordinate(); coordinate.setLatitude(45); coordinate.setLongitude(45); coordinate.setAltitude(1000); return coordinate; } @Override public GeocentricCoordinate unmarshal(GeodeticCoordinate arg0) throws Exception { // TODO do a real coordinate transformation GeocentricCoordinate gcc = new GeocentricCoordinate(); gcc.setX(100); gcc.setY(200); gcc.setZ(300); return gcc; } }
Objectwithlocation field
package testJaxb; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class ObjectWithLocation { private GeocentricCoordinate location = new GeocentricCoordinate(); public GeocentricCoordinate getLocation() { return location; } public void setLocation(GeocentricCoordinate location) { this.location = location; } public static void main(String[] args) { ObjectWithLocation object = new ObjectWithLocation(); try { JAXBContext context = JAXBContext.newInstance(ObjectWithLocation.class,GeodeticCoordinate.class,GeocentricCoordinate.class); Marshaller marshaller = context.createMarshaller(); marshaller.setAdapter(new GeocentricToGeodeticLocationAdapter()); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true); marshaller.marshal(object,System.out); } catch (JAXBException jaxb) { jaxb.printStackTrace(); } } }
Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <objectWithLocation> <location z="0.0" y="0.0" x="0.0"/> </objectWithLocation>
By using comments (in my package info.java file):
@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters ({ @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=GeocentricToGeodeticLocationAdapter.class,type=GeocentricCoordinate.class),}) package package testJaxb;
I get the following (required) XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <objectWithLocation> <location longitude="45.0" latitude="45.0" altitude="1000.0"/> </objectWithLocation>
So my question is twofold
>Why does the adapter work when commenting, but not when explicitly set through the setadapter method? > If I have a class that cannot be annotated, and my package info Java I can't modify to add comments. How can I solve this problem?
Solution
Setadapter (xmladapter) on Marshall is used to pass the initialization xmladapter of the property that has been annotated with @ xmljavatypeadapter The following link is an answer to my use of this behavior:
> Using JAXB to cross reference XmlIDs from two XML files
If you want to map third-party classes, you can use the XML Mapping file of eclipse link JAXB (Moxy) (I am the director of Moxy):
> http://bdoughan.blogspot.com/2010/12/extending-jaxb-representing-annotations.html