java - jaxb2marshaller - ¿Cuándo JAXB unmarshaller.unmarshal devuelve un JAXBElement<MySchemaObject> o un MySchemaObject?
unmarshaller spring (5)
Debe agregar a su clase generada JAXB @XMLRootElement
- debe tener espacio de nombres:
@XmlRootElement(namespace="http://your.namespace.com/", name="yourRootElement")
Echa un vistazo a la pregunta relacionada (hay muchos consejos buenos): Class Cast Exception cuando intentas desmarcar todos los XML.
Tengo dos códigos, en dos proyectos Java diferentes, que hacen casi lo mismo, (sin distorsionar la entrada de un servicio web de acuerdo con un archivo xsd).
Pero en un caso debería escribir esto: (La entrada es un nombre de marcador de posición) (el elemento es la entrada de elemento de elemento)
ClassLoader clInput = input.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("input", clInput);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() );
y en la otra biblioteca, debo usar JAXBElement.getValue (), porque es un JAXBElement que se devuelve, y una conversión simple (de entrada) simplemente se bloquea:
Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() ).getValue();
¿Sabes lo que lleva a tal diferencia?
Depende de la presencia de la anotación XmlRootElement en la clase de su elemento raíz.
Si genera sus clases JAXB desde un XSD, se aplican las siguientes reglas:
- si el tipo del elemento raíz es un tipo anónimo -> la anotación XmlRootElement se agrega a la clase generada
- si el tipo del elemento raíz es un tipo de nivel superior -> la anotación XmlRootElement se omite en la clase generada
Por esa razón a menudo elijo tipos anónimos para elementos raíz.
Puede personalizar el nombre de clase de este tipo anónimo con un archivo de personalización. Por ejemplo, crear un archivo bindings.xjc como este:
<jxb:bindings version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:bindings schemaLocation="yourXsd.xsd" node="/xs:schema">
<jxb:bindings node="//xs:element[@name=''yourRootElement'']">
<jxb:class name="YourRootElementType"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Modificando las clases generadas en java, no estoy de acuerdo. No permitiendo todo el formato xsd posible, no estoy de acuerdo.
Gracias a todas sus explicaciones y enlaces, este es el código que he escrito para ocuparme de ambos casos, utilizando la introspección de anotación. Funciona tanto para la salida como para la entrada, y es (a mi parecer) más genérico:
public class JaxbWrapper {
private static boolean isXmlRootElement(Class classT){
Annotation[] annotations = classT.getAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof XmlRootElement){
return true;
}
}
return false;
}
public static Object unmarshall(Class classObjectFactory, Class classObject, XMLStreamReader xmlStreamReader){
Package pack = classObjectFactory.getPackage();
String strPackageName = pack.getName();
Object returnObject = null;
try {
JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());
Unmarshaller unmarshaller = jc.createUnmarshaller();
returnObject = unmarshaller.unmarshal( xmlStreamReader );
boolean bIsRootedElement = isXmlRootElement(classObject);
if(!bIsRootedElement)
{
JAXBElement jaxbElement = (JAXBElement) returnObject;
returnObject = jaxbElement.getValue();
}
}
catch (JAXBException e) {
/*...*/
}
return returnObject;
}
private static void writeToXml(Class classObjectFactory, Object obj, XMLStreamWriter xmlStreamWriter){
Package pack = classObjectFactory.getPackage();
String strPackageName = pack.getName();
try {
JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(obj, xmlStreamWriter);
}
catch(JAXBException e) {
/*...*/
}
}
public static String marshall(Class classObjectFactory, Class classObject, Object obj){
Object objectToMarshall = obj;
boolean bIsRootedElement = isXmlRootElement(classObject);
if(!bIsRootedElement)
{
Package pack = classObjectFactory.getPackage();
String strPackageName = pack.getName();
String strClassName = classObject.getName();
QName qName = new QName(strPackageName, strClassName);
JAXBElement jaxbElement = new JAXBElement(qName, classObject, null, obj);
objectToMarshall = jaxbElement;
}
StringWriter sw = new StringWriter();
XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
XMLStreamWriter xmlStreamWriter = null;
try {
xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(sw);
writeToXml(classObjectFactory, objectToMarshall, xmlStreamWriter);
xmlStreamWriter.flush();
xmlStreamWriter.close();
}
catch (XMLStreamException e) {
/*...*/
}
return sw.toString();
}
}
Si el elemento raíz corresponde de manera única a una clase de Java, se devolverá una instancia de esa clase y, de no ser JAXBElement
, se devolverá un JAXBElement
.
Si desea asegurarse de obtener siempre una instancia del objeto de dominio, puede aprovechar JAXBInstrospector
. A continuación se muestra un ejemplo.
Manifestación
package forum10243679;
import java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
private static final String XML = "<root/>";
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBIntrospector jaxbIntrospector = jc.createJAXBIntrospector();
Object object = unmarshaller.unmarshal(new StringReader(XML));
System.out.println(object.getClass());
System.out.println(jaxbIntrospector.getValue(object).getClass());
Object jaxbElement = unmarshaller.unmarshal(new StreamSource(new StringReader(XML)), Root.class);
System.out.println(jaxbElement.getClass());
System.out.println(jaxbIntrospector.getValue(jaxbElement).getClass());
}
}
Salida
class forum10243679.Root
class forum10243679.Root
class javax.xml.bind.JAXBElement
class forum10243679.Root
Tengo el mismo problema. JAXB unmarshaller.unmarshal devuelve JAXBElement<MyObject>
lugar del MyObject
deseado.
Encontré y @XmlElementDecl
. El problema esta resuelto.