xmlrootelement tutorial tipo sirve saxexception2 que podido partir para marshaller istack generar example clases canalizar java xml spring jaxb

java - tipo - jaxb tutorial



Defina los espacios de nombres Spring JAXB sin usar NamespacePrefixMapper (4)

(Respuesta muy editada)

Creo que el problema en su código se debe a algunos desajustes de URI de espacio de nombres. A veces está utilizando " http://www.example.org/abc " y otras veces "www.example.org/abc" . Lo siguiente debería hacer el truco:

Main.java

package org.example.domain; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; public class Main { public static void main(String[] args) throws JAXBException { JAXBContext jc = JAXBContext.newInstance(RootElement.class); System.out.println(jc); RootElement re = new RootElement(); re.childElementWithXlink = new ChildElementWithXlink(); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(re, System.out); } }

RootElement.java

package org.example.domain; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(namespace="http://www.example.org/abc", name="Root_Element") public class RootElement { @XmlElement(namespace = "http://www.example.org/abc") public ChildElementWithXlink childElementWithXlink; }

ChildElementWithXLink.java

package org.example.domain; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSchemaType; @XmlRootElement(namespace="http://www.example.org/abc", name="Child_Element_With_XLink") public class ChildElementWithXlink { @XmlAttribute(namespace = "http://www.w3.org/1999/xlink") @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI") private String href="http://www.example.org"; }

package-info.java

@javax.xml.bind.annotation.XmlSchema( namespace = "http://www.example.org/abc", xmlns = { @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"), @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink") }, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package org.example.domain;

Ahora ejecutando Main.main () da el siguiente resultado:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <abc:Root_Element xmlns:abc="http://www.example.org/abc" xmlns:xlink="http://www.w3.org/1999/xlink"> <abc:childElementWithXlink xlink:href="http://www.example.org"/> </abc:Root_Element>

[Muy editado a medida que avanza la comprensión]

¿Es posible hacer que Spring Jaxb2Marshaller use un conjunto personalizado de prefijos de espacio de nombres (o al menos respete los que figuran en el archivo de esquema / anotaciones) sin tener que usar una extensión de un NamespacePrefixMapper?

La idea es tener una clase con una relación "tiene una" con otra clase que a su vez contiene una propiedad con un espacio de nombres diferente. Para ilustrar mejor esto, considere el siguiente esquema del proyecto que usa JDK1.6.0_12 (lo último que puedo obtener en el trabajo) Tengo lo siguiente en el paquete org.example.domain:

Main.java:

package org.example.domain; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; public class Main { public static void main(String[] args) throws JAXBException { JAXBContext jc = JAXBContext.newInstance(RootElement.class); RootElement re = new RootElement(); re.childElementWithXlink = new ChildElementWithXlink(); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(re, System.out); } }

RootElement.java:

package org.example.domain; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(namespace = "www.example.org/abc", name="Root_Element") public class RootElement { @XmlElement(namespace = "www.example.org/abc") public ChildElementWithXlink childElementWithXlink; }

ChildElementWithXLink.java:

package org.example.domain; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSchemaType; @XmlRootElement(namespace="www.example.org/abc", name="Child_Element_With_XLink") public class ChildElementWithXlink { @XmlAttribute(namespace = "http://www.w3.org/1999/xlink") @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI") private String href="http://www.example.org"; }

package-info.java:

@javax.xml.bind.annotation.XmlSchema( namespace = "http://www.example.org/abc", xmlns = { @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"), @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink") }, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package org.example.domain;

Al ejecutar Main.main () se obtiene el siguiente resultado:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ns2:Root_Element xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="www.example.org/abc"> <ns2:childElementWithXlink ns1:href="http://www.example.org"/> </ns2:Root_Element>

mientras que lo que me gustaría es:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <abc:Root_Element xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:abc="www.example.org/abc"> <abc:childElementWithXlink xlink:href="http://www.example.org"/> </abc:Root_Element>

Una vez que esta parte está funcionando, entonces el problema pasa a configurar el Jaxb2Marshaller en Spring (Spring 2.5.6, con spring-oxm-tiger-1.5.6 que proporciona Jaxb2Marshaller) para que proporcione lo mismo por medio de una configuración de contexto simple y una llamada al mariscal ().

¡Gracias por su continuo interés en este problema!



La implementación de JAXB en JDK 7 admite el prefijo de espacio de nombres. He intentado con JDK 1.6.0_21 sin suerte.


[Algunas ediciones para ofrecer una alternativa JAXB-RI se encuentran al final de este post]

Bueno, después de mucho rascarme la cabeza finalmente tuve que aceptarlo para mi entorno (JDK1.6.0_12 en Windows XP y JDK1.6.0_20 en Mac Leopard) Simplemente no puedo hacer que esto funcione sin recurrir al mal que es el NamespacePrefixMapper . ¿Por qué es malo? Porque obliga a depender de una clase JVM interna en su código de producción. Estas clases no forman parte de una interfaz confiable entre la JVM y su código (es decir, cambian entre las actualizaciones de la JVM).

En mi opinión, Sun debería abordar este problema o alguien con un conocimiento más profundo podría agregar algo a esta respuesta.

Seguir adelante Dado que NamespacePrefixMapper no debe utilizarse fuera de la JVM, no se incluye en la ruta de compilación estándar de javac (una subsección de rt.jar controlada por ct.sym). Esto significa que cualquier código que dependa de él probablemente se compilará bien en un IDE, pero fallará en la línea de comandos (es decir, Maven o Ant). Para superar esto, el archivo rt.jar debe incluirse explícitamente en la compilación, e incluso entonces Windows parece tener problemas si la ruta tiene espacios en ella.

Si te encuentras en esta posición, aquí hay un fragmento de Maven que te sacará de los problemas:

<dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.1.9</version> <scope>system</scope> <!-- Windows will not find rt.jar if it is in a path with spaces --> <systemPath>C:/temp/rt.jar</systemPath> </dependency>

Tenga en cuenta el camino codificado duro de la basura a un lugar extraño para rt.jar. Podría evitar esto con una combinación de {java.home} /lib/rt.jar que funcionará en la mayoría de los sistemas operativos pero no está garantizado debido al problema del espacio de Windows. Sí, puedes usar perfiles y activar en consecuencia ...

Alternativamente, en Ant puedes hacer lo siguiente:

<path id="jre.classpath"> <pathelement location="${java.home}/lib" /> </path> // Add paths for build.classpath and define {src},{target} as usual <target name="compile" depends="copy-resources"> <mkdir dir="${target}/classes"/> <javac bootclasspathref="jre.classpath" includejavaruntime="yes" debug="on" srcdir="${src}" destdir="${target}/classes" includes="**/*"> <classpath refid="build.classpath"/> </javac> </target>

¿Y qué hay de la configuración de Jaxb2Marshaller Spring? Bueno, aquí está, completo con mi propio NamespacePrefixMapper:

Primavera:

<!-- JAXB2 marshalling (domain objects annotated with JAXB2 meta data) --> <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="contextPaths"> <list> <value>org.example.domain</value> </list> </property> <property name="marshallerProperties"> <map> <!-- Good for JDK1.6.0_6+, lose ''internal'' for earlier releases - see why it''s evil? --> <entry key="com.sun.xml.internal.bind.namespacePrefixMapper" value-ref="myCapabilitiesNamespacePrefixMapper"/> <entry key="jaxb.formatted.output"><value type="boolean">true</value></entry> </map> </property> </bean> <!-- Namespace mapping prefix (ns1->abc, ns2->xlink etc) --> <bean id="myNamespacePrefixMapper" class="org.example.MyNamespacePrefixMapper"/>

Entonces mi NamespacePrefixMapper código:

public class MyNamespacePrefixMapper extends NamespacePrefixMapper { public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { if (requirePrefix) { if ("http://www.example.org/abc".equals(namespaceUri)) { return "abc"; } if ("http://www.w3.org/1999/xlink".equals(namespaceUri)) { return "xlink"; } return suggestion; } else { return ""; } } }

Pues ahí está. Espero que esto ayude a alguien a evitar el dolor por el que pasé. Oh, por cierto, puedes encontrar la siguiente excepción si usas el enfoque malvado anterior en Jetty:

java.lang.IllegalAccessError: la clase sun.reflect.GeneratedConstructorAccessor23 no puede acceder a su superclase sun.reflect.ConstructorAccessorImpl

Así que buena suerte con eso. Pista: rt.jar en la ruta de inicio de tu servidor web.

[Ediciones extra para mostrar el enfoque JAXB-RI (Implementación de referencia)]

Si puede introducir las bibliotecas JAXB-RI en su código, puede hacer las siguientes modificaciones para obtener el mismo efecto:

Principal:

// Add a new property that implies external access marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());

MyNamespacePrefixMapper:

// Change the import to this import com.sun.xml.bind.marshaller.NamespacePrefixMapper;

Agregue el siguiente JAR desde la descarga de JAXB-RI (después de saltar a través de aros de licencias) desde la carpeta / lib:

jaxb-impl.jar

Al ejecutar Main.main () se obtiene el resultado deseado.