2013-05-07 256 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