Java 类型为泛型类型参数的JAXB编组字段

Java 类型为泛型类型参数的JAXB编组字段,java,xml,generics,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>

我希望下面的测试能够与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>"));
    }

}
公共类GenericFieldMarshallTest{
公共静态类CustomType{
}
公共静态类CustomTypeAdapter扩展了XmlAdapter{
@凌驾
公共字符串封送处理(CustomType v)引发异常{
返回“CustomType”;
}
@凌驾
公共CustomType解组(字符串v)引发异常{
返回新的CustomType();
}
}
@XmlJavaTypeAdapter(type=CustomType.class,value=CustomTypeAdapter.class)
公共静态类根元素{
@XmlValue公共价值类型值;
}
@XmlRootElement(name=“root”)
公共静态类CustomRootElement扩展了RootElement{
公共CustomRootElement(){
值=新的CustomType();
}
}
@试验
public void test()引发异常{
JAXBContext context=JAXBContext.newInstance(CustomRootElement.class,
CustomType.class、RootElement.class);
StringWriter w=新的StringWriter();
context.createMarshaller().marshal(新的CustomRootElement(),w);
资产(w.toString(),equalTo(“CustomType”);
}
}
我得到的例外是:

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)
java.lang.NullPointerException
位于com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:165)
位于com.sun.xml.bind.v2.runtime.property.ValueProperty。(ValueProperty.java:77)
位于com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:106)
位于com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl。(ClassBeanInfoImpl.java:179)
位于com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515)
位于com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.(ClassBeanInfoImpl.java:166)
位于com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:515)
位于com.sun.xml.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:330)
位于com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140)
位于com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154)
位于com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121)
在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)处
位于sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)中
位于java.lang.reflect.Method.invoke(Method.java:597)
位于javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:202)
位于javax.xml.bind.ContextFinder.find(ContextFinder.java:363)
位于javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
位于javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522)
原因似乎是JAXB不知道如何封送我的字段的声明类型(我认为它在运行时被擦除为Object?),即使在运行时我只将字段设置为JAXB知道的类型

如何封送类型为泛型的字段


(用@XmlAttribute替换@XmlValue并不能修复异常,也不能将字段的声明类型更改为Object,当然,如果字段声明为String,则一切都可以正常工作,但字符串不能从CustomType赋值。@XmlJavaTypeAdapter的位置也没有区别;在我的实际代码中,它被设置为n package info.java中的包级别。)

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

然后您似乎必须将
@XmlJavaTypeAdapter
放在
CustomRootElement

此外,需要告知
JAXBContext
所有涉及的类。创建jaxb.index或
ObjectFactory
并通过为
newInstance
方法提供包名来创建上下文,或列出所有类

完整的代码(由于我使用了
main()
而不是JUnit测试方法,所以稍作修改):

我还在
CustomType
中添加了一个字段,它也可以工作。因此,如果您不需要漂亮的XML,这个解决方案应该足够了。
我希望我没有忘记我所做的任何更改。如果我忘记了,请留下评论,我将进行相应的编辑。

关于XmlAdapter类型的顺序,你是对的,我打算让它们反过来。当我为我的问题编写假示例时,这是一个转录错误。我在我的问题中解决了这个问题。但是,其余的问题都没有解决您的建议为我带来了工作代码。@XmlJavaTypeAdapter注释的位置是任意的;在我的实际代码中,它位于package-info.class的包级别。我已经尝试将其他涉及的类添加到JAXBContext.newInstance()中如果JAXB自己找不到它们,请调用,但这对问题也没有影响。如果我的问题中有一个测试的工作版本,请您将它放在一个pastebin中并链接到这里,这样我们就可以看到为了让它工作您更改了什么?我编辑了这篇文章。不需要将pastebin这样的外部依赖项引入本文中。我希望这对您有用,因为我使用了JAXB,因为它与JDK一起分发。感谢您更新了答案。不幸的是,您遗漏了RootElement字段上的@XmlValue注释。当我重新添加该注释时(因为我需要字段的值成为XML元素的内容)我看到了我粘贴的原始异常。我想这意味着我误用了@XmlValue,或者Sun的JAXB连接到@XmlValue时出现了一个bug。老实说,我从来没有使用过@XmlValue,在阅读文档后,我真的不明白为什么需要它。如果确实需要的话
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);
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <value xsi:type="customType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</root>