2013-05-07 261 views
6

我想读像一个JSON文件:JAXB和继承

{ 
    "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。

编辑:

基于由感知所提供的代码,我做了一个快速的尝试......不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; 
    } 
} 

和一个简单的职位,需要它:

@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: [email protected]; line: 2, column: 5] (through reference chain: DataWrapper["data"]) 

回答

11

DataClass添加@XmlSeeAlso批注,用于指定所有子类:

@XmlRootElement 
@XmlSeeAlso({DataSubA.class, DataSubB.class}) 
public abstract class Data implements Serializable { 

然后在每个子类中使用@XmlType批注指定类型名称。

@XmlType(name="1") 
public class DataSubA extends Data { 

UPDATE

注:我是EclipseLink JAXB (MOXy)铅和JAXB (JSR-222)专家小组的成员。 JAXB(JSR-222)规范不包括JSON绑定。有不同的方式JAX-RS允许你通过JAXB注释指定JSON映射:

  1. 一个JAXB实现加上像抛弃库,转换的StAX事件JSON(参见:http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html
  2. 通过利用一个JAXB IMPL它提供了JSON绑定(请参阅:http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
  3. 利用JSON绑定工具提供对某些JAXB元数据(即Jackson)的支持。

由于您的模型似乎并没有像预期的那样我猜你正在使用的场景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

要指定莫西,因为你需要包含一个文件在同一个包中的以下项域模型称为jaxb.properties您的JAXB提供商(请参阅: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实现中使用莫西。

+0

没有变化...: -/ – TofuBeer 2013-05-08 00:33:42

+0

@TofuBeer - 尝试添加一个'@ GET'看到响应消息是什么。然后,您可以将此与您作为请求发送的内容进行比较。 – 2013-05-08 00:37:25

+0

{ “数据”:{ “名”: “A”, “时代”:42 }} – TofuBeer 2013-05-08 03:17:51