【问题标题】:JAXB marshalling fields whose type is a generic type parameter类型为泛型类型参数的 JAXB 编组字段
【发布时间】:2010-06-29 10:15:46
【问题描述】:

我希望以下测试适用于 Sun 的 JAXB RI 2.2.1.1,但在构造 JAXBContext 时它会失败并出现 NullPointerException:

public class GenericFieldMarshallTest {

    public static class CustomType {
    }

    public static class CustomTypeAdapter extends XmlAdapter<String, CustomType> {
        @Override
        public String marshal(CustomType v) throws Exception {
            return "CustomType";
        }
        @Override
        public CustomType unmarshal(String v) throws Exception {
            return new CustomType();
        }
    }

    @XmlJavaTypeAdapter(type = CustomType.class, value = CustomTypeAdapter.class)
    public static class RootElement<ValueType> {
        @XmlValue public ValueType value;
    }

    @XmlRootElement(name = "root")
    public static class CustomRootElement extends RootElement<CustomType> {
        public CustomRootElement() {
            value = new CustomType();
        }
    }

    @Test
    public void test() throws Exception {
        JAXBContext context = JAXBContext.newInstance(CustomRootElement.class,
                CustomType.class, RootElement.class);
        StringWriter w = new StringWriter();
        context.createMarshaller().marshal(new CustomRootElement(), w);
        assertThat(w.toString(), equalTo("<root>CustomType</root>"));
    }

}

我得到的例外是:

java.lang.NullPointerException
        at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:165)
        at com.sun.xml.bind.v2.runtime.property.ValueProperty.<init>(ValueProperty.java:77)
        at com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:106)
        at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:179)
        at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515)
        at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:166)
        at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515)
        at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:330)
        at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140)
        at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154)
        at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:202)
        at javax.xml.bind.ContextFinder.find(ContextFinder.java:363)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522)

原因似乎是 JAXB 不知道如何编组我的字段的声明类型(我认为它在运行时被擦除为 Object ?)即使在运行时我只将字段设置为 JAXB 的类型知道。

如何编组类型为泛型的字段?

(用@XmlAttribute 替换@XmlValue 并不能修复异常,也不能将字段的声明类型更改为Object,当然如果字段声明为String,则一切正常,除了String 不能从CustomType 分配.@XmlJavaTypeAdapter 的位置也没有区别;在我的实际代码中,它是在 package-info.java 中的包级别设置的。)

【问题讨论】:

    标签: java xml generics jaxb


    【解决方案1】:

    首先:您的XmlAdapter 是错误的。泛型类型则相反。

    那么您似乎必须将@XmlJavaTypeAdapter 放在CustomRootElement 上。

    此外,JAXBContext 需要被告知所有涉及的课程。创建一个 jaxb.in​​dex 或 ObjectFactory 并通过将包名称提供给 newInstance 方法或列出所有类来创建上下文。

    完整代码(稍作修改,因为我使用main() 而不是JUnit 测试方法):

    public static class CustomType {
    }
    
    public static class CustomTypeAdapter extends
            XmlAdapter<String, CustomType> {
    
        @Override
        public String marshal(CustomType v) throws Exception {
            return "CustomType";
        }
    
        @Override
        public CustomType unmarshal(String v) throws Exception {
            return new CustomType();
        }
    
    }
    
    public static class RootElement<V> {
        public V value;
    }
    
    @XmlJavaTypeAdapter(CustomTypeAdapter.class)
    @XmlRootElement(name = "root")
    public static class CustomRootElement extends RootElement<CustomType> {
        public CustomRootElement() {
            value = new CustomType();
        }
    }
    
    public static void main(String[] args) throws Exception {
        JAXBContext context = JAXBContext.newInstance(CustomRootElement.class,
                CustomType.class, RootElement.class);
        StringWriter w = new StringWriter();
        CustomRootElement cre = new CustomRootElement();
        cre.value = new CustomType();
    
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
        marshaller.marshal(cre, w);
    
        System.err.println(w.toString());
    
        // just to see whether unmarshalling works too
        CustomRootElement c = (CustomRootElement) context.createUnmarshaller()
                .unmarshal(new StringReader(w.toString()));
        System.err.println(c.value);
    }
    

    现在,编组 CustomRootElement 的结果不是您在测试中所期望的(也不是我所期望的),但您可以解组它并获得您之前编组的结果。所以(取消)编组两者都可以工作,但 XML 看起来不太好:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <root>
        <value xsi:type="customType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
    </root>
    

    我还在CustomType 中添加了一个字段,并且效果也很好。因此,如果您不需要漂亮的 XML,这个解决方案就足够了。 我希望我没有忘记我所做的任何改变。如果我这样做了,请发表评论,我会相应地进行编辑。

    【讨论】:

    • 您对 XmlAdapter 类型的顺序是正确的,我打算让它们反过来。当我为我的问题编写虚假示例时,这是一个转录错误。我已经在我的问题中解决了这个问题。但是,您的其他建议都没有为我带来工作代码。 @XmlJavaTypeAdapter 注解的位置是任意的;在我的实际代码中,它位于 package-info.class 中的包级别。我已经尝试将涉及的其他类添加到 JAXBContext.newInstance() 调用中,以防 JAXB 自己找不到它们,但这也对问题没有影响。
    • 如果您在我的问题中有一个工作版本的测试,您能把它放在一个 pastebin 中并在这里链接到它,这样我们就可以看到您为了让它工作而做了什么更改?
    • 我编辑了这篇文章。不需要将诸如 pastebin 之类的外部依赖项引入其中。我希望这对你有用,因为我使用了 JAXB,因为它是随 JDK 一起分发的。
    • 感谢您更新您的答案。不幸的是,您在 RootElement 字段上遗漏了 @XmlValue 注释。当我重新添加它时(因为我需要字段的值成为 XML 元素的内容),我看到了我粘贴的原始异常。我想这意味着要么我滥用了@XmlValue,要么Sun 的JAXB 以某种方式连接到@XmlValue 存在错误。
    • 好吧,老实说,我从来没有使用过@XmlValue,在阅读了文档之后,我真的不明白为什么你会在这里需要它。如果确实有必要,我想我无法提供进一步的帮助,抱歉。
    猜你喜欢
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多