java - 类型为通用类型参数的 JAXB 编码字段

标签 java xml generics jaxb

我希望以下测试可以与 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 不知道如何编码我的字段的声明类型(我认为它在运行时被删除为对象?)即使在运行时我只将字段设置为 JAXB 的类型意识到。

如何编码一个类型为通用的字段?

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

最佳答案

首先:您的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,此解决方案就足够了。 我希望我没有忘记我所做的任何更改。如果我这样做了,请发表评论,我会相应地进行编辑。

关于java - 类型为通用类型参数的 JAXB 编码字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3139722/

相关文章:

java - 为什么这段代码抛出 java.lang.StackOverflowError

java - 并发操作和线程

html - 关闭斜线之前的空间?

json - JSON 字符串中是否允许换行?

java - 作为字符串的函数名称

Java:将字符串拆分为单独的字符串和整数变量

java - JPA:未通过 persist() 在数据库中创建实体

java - XPath 中的 XML 子位置与文档位置

c# - 将派生类集合分配给基类集合编译错误

java - 无法将通用项实例化为列表 Java