entiendo todo sobre cómo utilizar XMLAdapters a convert unmappable types, o simplemente para cambiar cómo ciertos objetos son serializados/deserializado a XML. Todo funciona bien si utilizo anotaciones (ya sea a nivel de paquete o de otro modo). El problema es que estoy intentando cambiar las representaciones de objetos de terceros a los que no puedo cambiar el código fuente (es decir, para insertar anotaciones).JAXB adaptadores XML funcionan a través de anotaciones, pero no a través de setAdapter
Eso no debería ser un problema, teniendo en cuenta que el objeto Marshaller tiene un método para manually adding adapters. Desafortunadamente, no importa lo que haga, no puedo configurar los adaptadores de esta manera para que entren. Por ejemplo, tengo una clase que representa un punto en el espacio XYZ (coordenadas geocéntricas). En el XML que produzco, quiero que se convierta en latitud/longitud/altitud (coordenadas geodésicas). Aquí están mis clases:
Geocéntrica
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;
}
}
geodésicos
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,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;
}
}
campo ObjectWithLocation
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();
}
}
}
Salida:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithLocation>
<location z="0.0" y="0.0" x="0.0"/>
</objectWithLocation>
Mediante el uso de una anotación (en mi archivo package-info.java
):
@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters
({
@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=GeocentricToGeodeticLocationAdapter.class,type=GeocentricCoordinate.class),
})
package package testJaxb;
consigo el código XML siguiente (deseada)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithLocation>
<location longitude="45.0" latitude="45.0" altitude="1000.0"/>
</objectWithLocation>
Así que mi pregunta es doble.
- ¿Por qué funciona el adaptador anotado, pero no cuando se establece explícitamente a través del método
setAdapter
? - ¿Cómo puedo evitar este problema cuando tengo clases que no puedo anotar y cuyo package-info.java no puedo modificar para agregar las anotaciones?
Ah bien. Eso es molesto. Ahora veo que no leí el contrato de addAdapter lo suficientemente bien. ¿Hay alguna razón para no exponer un método 'void addAdapter (Class classToAdapt, XMLAdapter )'? –
I82Much
@ I82Much - JAXB podría mejorarse para ofrecer ese comportamiento. La razón por la que actualmente no es compatible con esto se debe principalmente al rendimiento. JAXBContext puede inicializar sus metadatos en el momento de la creación y usar los mismos metadatos para cada operación de marshal/unmarshal. Si se permitiera la introducción de adaptadores en el nivel de Marshaller/Unmarshaller, una implementación de JAXB necesitaría tener en cuenta la posibilidad de tales cambios en los metadatos. –
Aceptar respuesta muy razonable. Estoy molesto porque es muy fácil hacer este tipo de personalización en XStream y estoy encontrando muchos más obstáculos para pasar con JAXB. Voy a verificar MOXy. Gracias por la ayuda. – I82Much