我正在尝试读取一个JSON文件,如:
{
"a": "abc",
"data" : {
"type" : 1,
...
}
}
其中......部分可根据类型替换:
{
"a": "abc",
"data" : {
"type" : 1,
"b" : "bcd"
}
}
或:
{
"a": "abc",
"data" : {
"type" : 2,
"c" : "cde",
"d" : "def",
}
}
对于我的生活,我无法弄清楚用于实现这一目的的正确的JAXB注释/类。 如果需要,我没有在数据块之外移动类型变量的问题。
我正在使用Glassfish 3.1.2.2。
编辑:
根据Perception提供的代码,我做了一个快速尝试......虽然在glassfish中不起作用:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type")
@JsonSubTypes(
{
@JsonSubTypes.Type(value = DataSubA.class, name = "1"),
@JsonSubTypes.Type(value = DataSubB.class, name = "2")
})
@XmlRootElement
public abstract class Data implements Serializable
{
private static final long serialVersionUID = 1L;
public Data()
{
super();
}
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class DataSubA
extends Data
{
private static final long serialVersionUID = 1L;
@XmlElement
private BigDecimal expenditure;
public DataSubA() {
super();
}
public DataSubA(final BigDecimal expenditure) {
super();
this.expenditure = expenditure;
}
@Override
public String toString() {
return String.format("%s[expenditure = %s]\n",
getClass().getSimpleName(), getExpenditure());
}
public BigDecimal getExpenditure() {
return expenditure;
}
public void setExpenditure(BigDecimal expenditure) {
this.expenditure = expenditure;
}
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class DataSubB
extends Data
{
private static final long serialVersionUID = 1L;
@XmlElement
private String name;
@XmlElement
private Integer age;
public DataSubB()
{
super();
}
public DataSubB(final String name, final Integer age)
{
super();
this.name = name;
this.age = age;
}
@Override
public String toString()
{
return String.format("%s[name = %s, age = %s]\n",
getClass().getSimpleName(), getName(), getAge());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class DataWrapper
{
@XmlElement
private Data data;
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
}
一个简单的POST将其收录:
@Stateless
@Path("x")
public class Endpoint
{
@POST
@Consumes(
{
MediaType.APPLICATION_JSON,
})
@Produces(
{
MediaType.APPLICATION_JSON,
})
public String foo(final DataWrapper wrapper)
{
return ("yay");
}
}
当我传入JSON时:
{
"data" :
{
"type" : 1,
"expenditure" : 1
}
}
我收到的消息如下:
Can not construct instance of Data, problem: abstract types can only be instantiated with additional type information
at [Source: org.apache.catalina.connector.CoyoteInputStream@28b92ec1; line: 2, column: 5] (through reference chain: DataWrapper["data"])
答案 0 :(得分:11)
在DataClass
上添加一个指定所有子类的@XmlSeeAlso
注释:
@XmlRootElement
@XmlSeeAlso({DataSubA.class, DataSubB.class})
public abstract class Data implements Serializable {
然后在每个子类上使用@XmlType
注释指定类型名称。
@XmlType(name="1")
public class DataSubA extends Data {
<强>更新强>
注意:我是EclipseLink JAXB (MOXy)主管,是JAXB (JSR-222)专家组的成员。
JAXB(JSR-222)规范不包括JSON绑定。 JAX-RS允许您通过JAXB注释指定JSON映射的方式有多种:
由于您的模型似乎没有按预期对注释做出反应,我猜您正在使用方案3.下面我将演示解决方案,就像您使用方案2一样。
<强> DataWrapper 强>
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class DataWrapper {
private String a;
private Data data;
}
数据强>
import javax.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({DataSubA.class, DataSubB.class})
public class Data {
}
<强> DataSubA 强>
import javax.xml.bind.annotation.XmlType;
@XmlType(name="1")
public class DataSubA extends Data {
private String b;
}
<强> DataSubB 强>
import javax.xml.bind.annotation.XmlType;
@XmlType(name="2")
public class DataSubB extends Data {
private String c;
private String d;
}
的 jaxb.properties 强>
要将MOXy指定为JAXB提供程序,您需要在与域模型相同的程序包中包含名为jaxb.properties
的文件,并带有以下条目(请参阅:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
<强>演示强>
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<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {DataWrapper.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum16429717/input.json");
DataWrapper dataWrapper = unmarshaller.unmarshal(json, DataWrapper.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(dataWrapper, System.out);
}
}
<强> input.json /输出强>
MOXy可以读取数值2
作为继承指示符,但目前它总是将其写为"2"
。我已打开以下增强请求来解决此问题:http://bugs.eclipse.org/407528。
{
"a" : "abc",
"data" : {
"type" : "2",
"c" : "cde",
"d" : "def"
}
}
了解更多信息
以下链接将帮助您在JAX-RS实现中使用MOXy。