java - parse - Herencia de JAXB, unmarshal a la subclase de la clase marshaled
parse xml java jaxb (6)
Estoy usando JAXB para leer y escribir XML. Lo que quiero es usar una clase JAXB base para clasificación y una clase JAXB heredada para desasignación. Esto es para permitir que una aplicación Java del remitente envíe XML a otra aplicación Java receptora. El emisor y el receptor compartirán una biblioteca JAXB común. Quiero que el receptor desmarque el XML en una clase JAXB específica del receptor que amplíe la clase JAXB genérica.
Ejemplo:
Esta es la clase JAXB común que utiliza el remitente.
@XmlRootElement(name="person")
public class Person {
public String name;
public int age;
}
Esta es la clase JAXB específica del receptor que se utiliza al desasociar el XML. La clase de receptor tiene lógica específica para la aplicación del receptor.
@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
public doReceiverSpecificStuff() ...
}
Marshalling funciona como se esperaba. El problema está en desasociar, aún deshace a Person
pesar de que JAXBContext usa el nombre del paquete del ReceiverPerson
subclasificado.
JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson);
Lo que quiero es unmarse a ReceiverPerson
. La única forma en que puedo hacer esto es eliminar @XmlRootElement
de Person
. Desafortunadamente, esto impide que la Person
sea ​​clasificada. Es como si JAXB comienza en la clase base y sigue su camino hasta que encuentra el primer @XmlRootElement
con el nombre apropiado. Intenté agregar un método createPerson()
que devuelve ReceiverPerson
a ObjectFactory
pero eso no ayuda.
No estoy seguro de por qué querrías hacer esto ... no me parece tan seguro.
Considere lo que pasaría en ReceiverPerson tiene variables de instancia adicionales ... entonces terminaría con (supongo) que esas variables sean nulas, 0 o falsas ... y que si nulo no está permitido o el número debe ser mayor que 0 ?
Creo que lo que probablemente quieras hacer es leer en la Persona y luego construir un nuevo ReceiverPerson a partir de eso (probablemente proporciones un constructor que tome una Persona).
Estás usando JAXB 2.0 ¿verdad? (desde JDK6)
Hay una clase:
javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>
cuál puede subclase y anula los siguientes métodos:
public abstract BoundType unmarshal(ValueType v) throws Exception;
public abstract ValueType marshal(BoundType v) throws Exception;
Ejemplo:
public class YourNiceAdapter
extends XmlAdapter<ReceiverPerson,Person>{
@Override public Person unmarshal(ReceiverPerson v){
return v;
}
@Override public ReceiverPerson marshal(Person v){
return new ReceiverPerson(v); // you must provide such c-tor
}
}
El uso se realiza de la siguiente manera:
@Your_favorite_JAXB_Annotations_Go_Here
class SomeClass{
@XmlJavaTypeAdapter(YourNiceAdapter.class)
Person hello; // field to unmarshal
}
Estoy bastante seguro de que, al usar este concepto, usted puede controlar el proceso de marshalling / unmarshalling usted mismo (incluida la elección del tipo correcto [sub | super] para construir).
Como realmente tiene dos aplicaciones separadas, compílelas con las diferentes versiones de la clase "Persona" - con la aplicación del receptor que no tiene @XmlRootElement(name="person")
en Person
. Esto no solo es feo, sino que también anula la capacidad de mantenimiento que usted desea utilizando la misma definición de Persona para el emisor y el receptor. Su única característica redentora es que funciona.
El siguiente fragmento es un método de una prueba de Junit 4 con luz verde:
@Test
public void testUnmarshallFromParentToChild() throws JAXBException {
Person person = new Person();
int age = 30;
String name = "Foo";
person.name = name;
person.age= age;
// Marshalling
JAXBContext context = JAXBContext.newInstance(person.getClass());
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(person, writer);
String outString = writer.toString();
assertTrue(outString.contains("</person"));
// Unmarshalling
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader reader = new StringReader(outString);
RecieverPerson reciever = (RecieverPerson)unmarshaller.unmarshal(reader);
assertEquals(name, reciever.name);
assertEquals(age, reciever.age);
}
La parte importante es el uso del JAXBContext.newInstance(Class... classesToBeBound)
para el contexto de descalificación:
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
Con esta llamada, JAXB calculará un cierre de referencia en las clases especificadas y reconocerá a RecieverPerson
. La prueba pasa. Y si cambia el orden de los parámetros, obtendrá una java.lang.ClassCastException
(por lo que debe pasar en este orden).
Subclase Persona dos veces, una para el receptor y otra para el remitente, y solo coloca el XmlRootElement en estas subclases (dejando la superclase, Person
, sin un XmlRootElement). Tenga en cuenta que tanto el emisor como el receptor comparten las mismas clases base de JAXB.
@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
// receiver specific code
}
@XmlRootElement(name="person")
public class SenderPerson extends Person {
// sender specific code (if any)
}
// note: no @XmlRootElement here
public class Person {
// data model + jaxb annotations here
}
[probado y confirmado para trabajar con JAXB]. Evita el problema que observa, cuando varias clases en la jerarquía de herencia tienen la anotación XmlRootElement.
Este es, sin duda, un enfoque más ordenado y más OO, ya que separa el modelo de datos común, por lo que no es una "solución" en absoluto.
Cree una ObjectFactory personalizada para instanciar la clase deseada durante el desemparejamiento. Ejemplo:
JAXBContext context = JAXBContext.newInstance("com.whatever.mypackage");
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.internal.bind.ObjectFactory", new ReceiverPersonObjectFactory());
return unmarshaller;
public class ReceiverPersonObjectFactory extends ObjectFactory {
public Person createPerson() {
return new ReceiverPerson();
}
}