MOXy反序列化exception:在项目中找不到具有默认根元素的描述符

这是我的课程:

@XmlRootElement(name="Zoo") class Zoo { //@XmlElementRef public Collection animals; } @XmlAccessorType(XmlAccessType.FIELD) @XmlSeeAlso({Bird.class, Cat.class, Dog.class}) @XmlDiscriminatorNode("@type") abstract class Animal { @XmlElement public String name; } @XmlDiscriminatorValue("Bird") @XmlRootElement(name="Bird") class Bird extends Animal { @XmlElement public String wingSpan; @XmlElement public String preferredFood; } @XmlDiscriminatorValue("Cat") @XmlRootElement(name="Cat") class Cat extends Animal { @XmlElement public String favoriteToy; } @XmlDiscriminatorValue("Dog") @XmlRootElement(name="Dog") class Dog extends Animal { @XmlElement public String breed; @XmlElement public String leashColor; } 

这是序列化的JSON:

  { "animals": [ { "type": "Bird", "name": "bird-1", "wingSpan": "6 feets", "preferredFood": "food-1" }, { "type": "Cat", "name": "cat-1", "favoriteToy": "toy-1" }, { "type": "Dog", "name": "dog-1", "breed": "bread-1", "leashColor": "black" } ] } 

这是解串器代码:

 public static  T Deserialize_Moxy(String jsonStr, Class[] cl) throws JAXBException { InputStream is = new ByteArrayInputStream(jsonStr.getBytes()); JAXBContext jc = JAXBContext.newInstance(cl); Unmarshaller unmarshaller = jc.createUnmarshaller(); // Marshal to JSON unmarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json"); unmarshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false); @SuppressWarnings("unchecked") T obj = (T)unmarshaller.unmarshal(is); return obj; } 

这是例外:

 Exception in thread "main" javax.xml.bind.UnmarshalException - with linked exception: [Exception [EclipseLink-25008] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.XMLMarshalException Exception Description: A descriptor with default root element was not found in the project] at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:1014) at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:147) at com.bp.samples.json.generics.Foo.Deserialize_Moxy(Foo.java:271) at com.bp.samples.json.generics.Foo.main(Foo.java:111) Caused by: Exception [EclipseLink-25008] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.XMLMarshalException Exception Description: A descriptor with default root element was not found in the project at org.eclipse.persistence.exceptions.XMLMarshalException.noDescriptorWithMatchingRootElement(XMLMarshalException.java:143) at org.eclipse.persistence.internal.oxm.record.SAXUnmarshallerHandler.startElement(SAXUnmarshallerHandler.java:222) at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parseRoot(JSONReader.java:161) at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:118) at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:827) at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:350) at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:334) at org.eclipse.persistence.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:407) at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:133) ... 2 more 

还有一个关于序列化JSON的问题:是否有办法让JSON序列化程序发布“@type”而不是“type”。 目前,它看起来像具有属性“类型”的对象。 如果我们可以用“@”来装饰它,那么更明显的是这更像是一个类型信息而不是一个属性。

谢谢,Behzad

以下是我对两个问题的回答:

问题#1 – 例外

当您使用MarshallerProperties.JSON_INCLUDE_ROOT属性关闭根元素时,您需要使用一个unmarshal方法,该方法使用Class参数告诉MOXy您要解组的对象的类型。

 StreamSource json = new StreamSource("src/forum14246033/input.json"); Zoo zoo = unmarshaller.unmarshal(json, Zoo.class).getValue(); 

问题2

还有一个关于序列化JSON的问题:是否有办法让JSON序列化程序发布“@type”而不是“type”。 目前,它看起来像具有属性“类型”的对象。 如果我们可以用“@”来装饰它,那么更明显的是这更像是一个类型信息而不是一个属性。

@前缀表示字段/属性映射到XML属性。 您可以使用JAXBContextProperties.JSON_ATTRIBUTE_PREFIX属性指定前缀以限定映射到XML属性的数据。

 properties.put(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, "@"); 

完整的例子

演示

 package forum14246033; import java.util.*; import javax.xml.bind.*; import javax.xml.transform.stream.StreamSource; import org.eclipse.persistence.jaxb.JAXBContextProperties; public class Demo { public static void main(String[] args) throws Exception { Map properties = new HashMap(2); properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json"); properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false); properties.put(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, "@"); JAXBContext jc = JAXBContext.newInstance(new Class[] {Zoo.class}, properties); Unmarshaller unmarshaller = jc.createUnmarshaller(); StreamSource json = new StreamSource("src/forum14246033/input.json"); Zoo zoo = unmarshaller.unmarshal(json, Zoo.class).getValue(); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(zoo, System.out); } } 

input.json /输出

 { "animals" : [ { "@type" : "Bird", "name" : "bird-1", "wingSpan" : "6 feets", "preferredFood" : "food-1" }, { "@type" : "Cat", "name" : "cat-1", "favoriteToy" : "toy-1" }, { "@type" : "Dog", "name" : "dog-1", "breed" : "bread-1", "leashColor" : "black" } ] } 

域模型

我不建议在您的域模型中使用公共字段,但如果您采用这种方式,则可以将元数据减少到以下内容:

动物园

 import java.util.Collection; class Zoo { public Collection animals; } 

动物

 import javax.xml.bind.annotation.XmlSeeAlso; import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode; @XmlSeeAlso({Bird.class, Cat.class, Dog.class}) @XmlDiscriminatorNode("@type") abstract class Animal { public String name; } 

 import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue; @XmlDiscriminatorValue("Bird") class Bird extends Animal { public String wingSpan; public String preferredFood; } 

jaxb.properties

要将MOXy指定为JAXB(JSR-222)提供程序,您需要在与域模型相同的包中包含一个名为jaxb.properties的文件,并带有以下条目:

 javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory