使用 JAXB 保持干燥

发布于 2024-11-04 23:59:37 字数 18528 浏览 4 评论 0原文

我正在开发许多 Java 类,它们必须按以下格式序列化为 XML:

<foo value="123"/>

<!-- or this -->
<bar value="abc"/>

<!-- or this -->
<baz value="true"/>

一开始,Foo.java 看起来像这样:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Foo {
    @XmlAttribute
    String value;

    // snip constructors

    // snip methods
    // getValue
    // equals, hashCode, toString
    // static valueOf(String), static valueOf(int)
}

不需要太多想象力就可以猜出>Bar.javaBaz.java 可能看起来像。这些是(在本示例中)intStringboolean 的非常简单的包装类。到目前为止,一切都很顺利。写完第九轮后,

class WhoCares {
    @XmlAttribute
    Whatever value;
    /* 2 constructors */
    /* 6 methods found in every one of these classes */
}

我想:“嘿,我知道如何解决这个问题 - 我只需要一个抽象级别!” 介绍 Wrapper

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
}

现在,要实现 Foo,我需要做的就是扩展 Wrapper,实现我需要的工厂方法:

class Foo extends Wrapper<String> {
    // snip constructors

    static Foo valueOf(String value) {
        return new Foo(value);
    }
}

并添加 @XmlSeeAlso({... Foo.班级...})Wrapper 的声明。


  • 优点:这好多了。
  • 缺点:JAXB 不会序列化这些对象。

JAXBContext.getInstance(Wrapper.class)JAXBContext.getInstance(Foo.class) 的任何调用> 由于在 JAXB 内部深处抛出 NullPointerException 而失败:

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]

我已经尝试将我的 JAXB 实现从 Metro 切换到 MOXy(并且我做了 检查实现是否已更改),但是 MOXy 会引发类似的 NPE:

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

有没有办法在最小化的同时生成所需的 XML样板并保持干燥

I'm developing a number of Java classes that must serialize to XML in the following format:

<foo value="123"/>

<!-- or this -->
<bar value="abc"/>

<!-- or this -->
<baz value="true"/>

In the beginning, Foo.java looked something like this:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Foo {
    @XmlAttribute
    String value;

    // snip constructors

    // snip methods
    // getValue
    // equals, hashCode, toString
    // static valueOf(String), static valueOf(int)
}

It doesn't take much imagination to guess what Bar.java and Baz.java might look like. These are very simple wrapper classes for (in this example) int, String, and boolean. Up to this point, everything's hunky dory. After writing the ninth round of

class WhoCares {
    @XmlAttribute
    Whatever value;
    /* 2 constructors */
    /* 6 methods found in every one of these classes */
}

I thought: "Hey, I know how I can solve this — I just need one level of abstraction!" Introducing 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
}

Now, to implement Foo, all I need to do is extend Wrapper, implement the factory methods I need:

class Foo extends Wrapper<String> {
    // snip constructors

    static Foo valueOf(String value) {
        return new Foo(value);
    }
}

and add an @XmlSeeAlso({... Foo.class ...}) to Wrapper's declaration.


  • Pro: this is much nicer.
  • Con: JAXB won't serialize these objects.

Any calls to JAXBContext.getInstance(Wrapper.class) or JAXBContext.getInstance(Foo.class) fail due to a NullPointerException thrown somewhere deep in the bowels of 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]

I've already tried switching my JAXB implementation from Metro to MOXy (and I did check that the implementation changed) but a similar NPE is thrown with 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

Is there any way to produce the required XML while minimizing boilerplate and staying DRY?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

眼泪也成诗 2024-11-11 23:59:37

您看到的问题是由于 Metro(参考实现)和 EclipseLink MOXy JAXB 实现。相关的 MOXy bug 是:

需要注意的一件事您的用例是,由于类型擦除,JAXB 实现会将值属性视为对象类型。这意味着编组操作将适合您,但解组操作将以字符串形式返回值。这就是为什么如果值字段使用 @XmlElement 进行注释,则将包含 xsi:type 信息以保留类型信息:

MOXy 解决方法

您可以通过利用以下 XmlAdapter 来解决 MOXy 错误:

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;
    }

}

XML 适配器在您的属性上注册,如下所示:

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;
    }

}

The issue you are seeing is due to bugs in both the Metro (reference implementation) and EclipseLink MOXy JAXB implementations. The relevant MOXy bug is:

One thing to note about your use case is that due to type erasure, a JAXB implementation is going to treat the value property as type Object. This means that marshal operations will work for you, but an unmarshal operation will return the value as a String. This is why if the value field was annotated with @XmlElement that xsi:type information would be included to preserve the type information:

MOXy Workaround

You workaround the MOXy bug by leveraging the following 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;
    }

}

The XML Adapter is registered on your property as follows:

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;
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文