2010-06-29 43 views
4

我希望下面的测试与Sun的JAXB RI 2.2.1.1工作,而是它失败,一个NullPointerException在构建的JAXBContext:JAXB编组场,它的类型是一个泛型类型参数

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知道的类型。

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

(用@XmlAttribute替换@XmlValue并不能修复异常,也不会修改字段的声明类型为Object,当然如果该字段被声明为String,那么一切正常,但String不能从CustomType。@XmlJavaTypeAdapter的放置也没有区别;在我的实际代码中,它在package-info.java中设置在包级别上。)

回答

2

首先:您的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,这个解决方案就足够了。 我希望我没有忘记我所做的任何改变。如果我这样做,只需留下评论,我会相应地编辑。

+0

你对XmlAdapter类型的顺序是正确的,我打算让它们成为另一种方式。当我为我的问题编写假示例时,这是一个转录错误。我已经解决了这个问题。 但是,其余的建议都不会导致我为其编写代码。 @XmlJavaTypeAdapter注释的位置是任意的;在我的实际代码中,它位于package-info.class的包级别。 我试过在JAXB无法找到它们本身的情况下,将涉及的其他类添加到JAXBContext.newInstance()调用中,但这对问题也没有任何影响。 – DanC 2010-06-29 12:17:46

+0

如果你在我的问题中有一个测试版本,你可以把它放在一个pastebin中并链接到它,所以我们可以看到你为了使它工作而改变了什么? – DanC 2010-06-29 12:18:37

+0

我编辑了帖子。不需要像pastebin那样引入外部依赖关系。我希望这适用于您,因为我使用JAXB,因为它随JDK一起分发。 – musiKk 2010-06-30 07:55:26