Mantenerse seco con Jaxb
-
28-10-2019 - |
Pregunta
Estoy desarrollando varias clases de Java que deben serializar a XML en el siguiente formato:
<foo value="123"/>
<!-- or this -->
<bar value="abc"/>
<!-- or this -->
<baz value="true"/>
Al principio, Foo.java
parecía algo así:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Foo {
@XmlAttribute
String value;
// snip constructors
// snip methods
// getValue
// equals, hashCode, toString
// static valueOf(String), static valueOf(int)
}
No se necesita mucha imaginación para adivinar qué Bar.java
y Baz.java
podría parecer. Estas son clases de envoltura muy simples para (en este ejemplo) int
, String
, y boolean
. Hasta este punto, todo es Hunky Dory. Después de escribir la novena ronda de
class WhoCares {
@XmlAttribute
Whatever value;
/* 2 constructors */
/* 6 methods found in every one of these classes */
}
Pensé: "Oye, sé cómo puedo resolver esto, ¡solo necesito un nivel de abstracción!" Introducción Wrapper<T>
:
abstract class Wrapper<T> {
@XmlAttribute
private T value;
Wrapper() {} // default ctor for JAXB
Wrapper(T value) {
this.value = value;
}
T getValue() {
return value;
}
// snip equals, hashCode, toString
}
Ahora, para implementar Foo
, todo lo que necesito hacer es extender Wrapper
, implementa los métodos de fábrica que necesito:
class Foo extends Wrapper<String> {
// snip constructors
static Foo valueOf(String value) {
return new Foo(value);
}
}
y agregar un @XmlSeeAlso({... Foo.class ...})
a Wrapper
Declaración.
- Pro: Esto es mucho más agradable.
- Estafa: Jaxb no serializa estos objetos.
Cualquier llamada a JAXBContext.getInstance(Wrapper.class)
o JAXBContext.getInstance(Foo.class)
fallar debido a un NullPointerException
arrojado en algún lugar en las intestinos de Jaxb:
17:06:03,706 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[localhost].[/Redacted].[JAX-RS Servlet]] Servlet.service() for servlet JAX-RS Servlet threw exception: java.lang.NullPointerException
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:165) [:2.2]
at com.sun.xml.bind.v2.runtime.property.AttributeProperty.<init>(AttributeProperty.java:87) [:2.2]
at com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:104) [:2.2]
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:179) [:2.2]
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515) [:2.2]
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:166) [:2.2]
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515) [:2.2]
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:534) [:2.2]
at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.<init>(SingleElementNodeProperty.java:101) [:2.2]
at sun.reflect.GeneratedConstructorAccessor49.newInstance(Unknown Source) [:1.6.0_24]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) [:1.6.0_24]
at java.lang.reflect.Constructor.newInstance(Constructor.java:513) [:1.6.0_24]
at com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:124) [:2.2]
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:179) [:2.2]
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515) [:2.2]
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:166) [:2.2]
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515) [:2.2]
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:330) [:2.2]
at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140) [:2.2]
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154) [:2.2]
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121) [:2.2]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [:1.6.0_24]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [:1.6.0_24]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [:1.6.0_24]
at java.lang.reflect.Method.invoke(Method.java:597) [:1.6.0_24]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:201) [:1.0.0.Final]
at javax.xml.bind.ContextFinder.find(ContextFinder.java:362) [:1.0.0.Final]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:618) [:1.0.0.Final]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:565) [:1.0.0.Final]
at com.sun.jersey.core.provider.jaxb.AbstractJAXBProvider.getStoredJAXBContext(AbstractJAXBProvider.java:189) [:1.6]
at com.sun.jersey.core.provider.jaxb.AbstractJAXBProvider.getJAXBContext(AbstractJAXBProvider.java:182) [:1.6]
at com.sun.jersey.core.provider.jaxb.AbstractJAXBProvider.getMarshaller(AbstractJAXBProvider.java:160) [:1.6]
at com.sun.jersey.core.provider.jaxb.AbstractJAXBProvider.getMarshaller(AbstractJAXBProvider.java:139) [:1.6]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:151) [:1.6]
at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:306) [:1.6]
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1310) [:1.6]
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1223) [:1.6]
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1213) [:1.6]
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414) [:1.6]
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537) [:1.6]
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:699) [:1.6]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [:1.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:324) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at com.example.mgp.filter.RetryFilter.doFilter(RetryFilter.java:109) [:]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at com.example.mgp.filter.AuthFilter.doFilter(AuthFilter.java:57) [:]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:67) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [:6.0.0.Final]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) [:6.0.0.Final]
at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:181) [:6.0.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285) [:1.1.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261) [:1.1.0.Final]
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88) [:6.0.0.Final]
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:100) [:6.0.0.Final]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) [:6.0.0.Final]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [:6.0.0.Final]
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158) [:6.0.0.Final]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [:6.0.0.Final]
at org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53) [:6.0.0.Final]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:654) [:6.0.0.Final]
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:951) [:6.0.0.Final]
at java.lang.Thread.run(Thread.java:662) [:1.6.0_24]
Ya he intentado cambiar mi implementación de Jaxb de Metro a Moxy (y lo hice Verifique que la implementación cambió) pero se lanza un NPE similar con moxy:
17:46:48,481 SEVERE [com.sun.jersey.spi.container.ContainerResponse] Mapped exception to response: 500 (Internal Server Error): javax.ws.rs.WebApplicationException: javax.xml.bind.MarshalException
- with linked exception:
[Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:159) [:1.6]
at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:306) [:1.6]
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1310) [:1.6]
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1223) [:1.6]
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1213) [:1.6]
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414) [:1.6]
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537) [:1.6]
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:699) [:1.6]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [:1.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:324) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at com.example.mgp.filter.RetryFilter.doFilter(RetryFilter.java:109) [:]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at com.example.mgp.filter.AuthFilter.doFilter(AuthFilter.java:57) [:]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:67) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [:6.0.0.Final]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) [:6.0.0.Final]
at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:181) [:6.0.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285) [:1.1.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261) [:1.1.0.Final]
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88) [:6.0.0.Final]
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:100) [:6.0.0.Final]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) [:6.0.0.Final]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [:6.0.0.Final]
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158) [:6.0.0.Final]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [:6.0.0.Final]
at org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53) [:6.0.0.Final]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:654) [:6.0.0.Final]
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:951) [:6.0.0.Final]
at java.lang.Thread.run(Thread.java:662) [:1.6.0_24]
Caused by: javax.xml.bind.MarshalException
- with linked exception:
[Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException]
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:314) [:2.2.0.v20110202-r8913]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:179) [:1.6]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:157) [:1.6]
... 36 more
Caused by: Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException
at org.eclipse.persistence.exceptions.XMLMarshalException.marshalException(XMLMarshalException.java:78) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:516) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:312) [:2.2.0.v20110202-r8913]
... 38 more
Caused by: java.lang.NullPointerException
at org.eclipse.persistence.oxm.record.OutputStreamRecord.outputStreamWrite(OutputStreamRecord.java:511) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.oxm.record.OutputStreamRecord.openStartElement(OutputStreamRecord.java:159) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XPathNode.startElement(XPathNode.java:330) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshalSingleValue(XMLCompositeObjectMappingNodeValue.java:185) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshal(XMLCompositeObjectMappingNodeValue.java:116) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:104) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:60) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:322) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.marshalAttributes(TreeObjectBuilder.java:338) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XPathNode.startElement(XPathNode.java:344) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshalSingleValue(XMLCompositeObjectMappingNodeValue.java:167) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.marshal(XMLCompositeObjectMappingNodeValue.java:116) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:104) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:60) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:322) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:325) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XMLAnyObjectMappingNodeValue.marshalSingleValue(XMLAnyObjectMappingNodeValue.java:154) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XMLAnyObjectMappingNodeValue.marshal(XMLAnyObjectMappingNodeValue.java:70) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.NodeValue.marshal(NodeValue.java:104) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(ObjectMarshalContext.java:60) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.XPathNode.marshal(XPathNode.java:322) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(TreeObjectBuilder.java:325) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:932) [:2.2.0.v20110202-r8913]
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:486) [:2.2.0.v20110202-r8913]
... 39 more
¿Hay alguna forma de producir el XML requerido mientras minimiza la calderera y la queda? SECO?
Solución
El problema que está viendo se debe a los errores tanto en el metro (implementación de referencia) como a Eclipselink moxy Implementaciones de JAXB. El error de moxy relevante es:
Una cosa a tener en cuenta sobre su caso de uso es que, debido al tipo de borrado, una implementación de JAXB tratará la propiedad de valor como objeto tipo. Esto significa que las operaciones de Marshal funcionarán para usted, pero una operación de desacuerdo devolverá el valor como una cadena. Es por eso que si el campo de valor se anotó con @xmlelement, se incluiría la información de tipo xsi: tipo para preservar la información del tipo:
Solución de moxy
Soloma el error de Moxy aprovechando el siguiente XMLAdapter:
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ObjectAdapter extends XmlAdapter<Object, Object> {
@Override
public Object unmarshal(Object v) throws Exception {
return v;
}
@Override
public Object marshal(Object v) throws Exception {
return v;
}
}
El adaptador XML está registrado en su propiedad de la siguiente manera:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlTransient
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso(Foo.class)
abstract class Wrapper<T> {
@XmlAttribute
@XmlJavaTypeAdapter(ObjectAdapter.class)
private T value;
Wrapper() {} // default ctor for JAXB
public Wrapper(T t) {
value = t;
}
}