我希望以下测试可以与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中的包级别设置的。)
答案 0 :(得分:2)
首先:您的XmlAdapter
错了。通用类型是另一种方式。
然后,您似乎必须将@XmlJavaTypeAdapter
放在CustomRootElement
上。
此外,需要告知JAXBContext
涉及的所有课程。创建一个jaxb.index或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,那么这个解决方案就足够了。
我希望我没有忘记我所做的任何改变。如果我这样做,只需发表评论,我就会进行相应的编辑。