2013-10-08 105 views
14

我在我的Spring MVC(v3.2.0.RELEASE)Web應用程序下面的對象模型多態性對象模型:反序列化JSON使用Spring和JsonTypeInfo註釋

public class Order { 
    private Payment payment; 
} 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT) 
@JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) 
public interface Payment {} 


@JsonTypeName("creditCardPayment") 
public class CreditCardPayment implements Payment {} 

當我連載訂單類JSON,我得到以下結果(這是我想要什麼):

{ 
    "payment" : { 
    "creditCardPayment": { 
     ... 
    } 
} 

不幸的是,如果我走在上面JSON,並嘗試去連載它放回我的對象​​模型,我得到下面的異常:

Could not read JSON: Could not resolve type id 'creditCardPayment' into a subtype of [simple type, class Payment] at [Source: [email protected]; line: 1, column: 58] (through reference chain: Order["payment"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id 'creditCardPayment' into a subtype of [simple type, class Payment] at [Source: [email protected]; line: 1, column: 58] (through reference chain: Order["payment"])

我的應用程序經由彈簧JavaConf配置,如下所示:

@Configuration 
@EnableWebMvc 
public class AppWebConf extends WebMvcConfigurerAdapter { 

    @Bean 
    public ObjectMapper objectMapper() { 
     ObjectMapper objectMapper = new ObjectMapper(); 
     objectMapper.setSerializationInclusion(Include.NON_NULL); 
     objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false); 
     return objectMapper; 
    } 

    @Bean 
    public MappingJackson2HttpMessageConverter mappingJacksonMessageConverter() { 
     MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); 
     converter.setObjectMapper(objectMapper()); 
     return converter; 
    } 

    @Bean 
    public Jaxb2RootElementHttpMessageConverter jaxbMessageConverter() { 
     return new Jaxb2RootElementHttpMessageConverter(); 
    } 

    @Override 
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 
     converters.add(jaxbMessageConverter()); 
     converters.add(mappingJacksonMessageConverter()); 
    } 
} 

爲了測試,我有2種方法的控制器,一個返回一個訂單HTTP GET請求(這一個作品)和一個通過HTTP POST(這一次失敗)接受訂單,例如

@Controller 
public class TestController { 

    @ResponseBody 
    @RequestMapping(value = "/test", method = RequestMethod.GET) 
    public Order getTest() {} 

    @RequestMapping(value = "/test", method = RequestMethod.POST) 
    public void postTest(@RequestBody order) {} 

} 

我試過所有關於SO的各種討論的建議,但到目前爲止沒有運氣。任何人都可以發現我做錯了什麼嗎?

回答

8

嘗試使用ObjectMapper.registerSubtypes註冊亞型,而不是使用註釋

+1

這工作!我從'Payment'接口中刪除了'@ JsonSubTypes.Type(name =「creditCardPayment」,value = CreditCardPayment.class),並在'AppWebConf'類中添加了'objectMapper.registerSubtypes(CreditCardPayment.class)'。令人失望的是,註釋不起作用,因爲我想保持我的配置一般,但至少它讓我通過這個問題。謝謝。 – grigori

+2

我面臨同樣的問題,上面的解決方案沒有幫助。 –

2

registerSubtypes()工作方法的!

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type") 
public interface Geometry { 
    //... 
} 

public class Point implements Geometry{ 
    //... 
} 
public class Polygon implements Geometry{ 
    //... 
} 
public class LineString implements Geometry{ 
    //... 
} 

GeoJson geojson= null; 
ObjectMapper mapper = new ObjectMapper(); 
mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); 
mapper.registerSubtypes(Polygon.class,LineString.class,Point.class); 

try { 
    geojson=mapper.readValue(source, GeoJson.class);    
} catch (IOException e) { 
    e.printStackTrace(); 
} 

注1:我們使用接口和實現類。我想讓傑克遜按照他們的實現類對類進行反序列化,你必須使用ObjectMapper的「registerSubtypes」方法註冊它們。注2:此外,您還可以使用「@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property =」type「)」作爲您的界面的註釋。

您還可以在映射器將POJO的值寫入爲json時定義屬性的順序。

這可以使用下面的註釋。

@JsonPropertyOrder({"type","crs","version","features"}) 
public class GeoJson { 

    private String type="FeatureCollection"; 
    private List<Feature> features; 
    private String version="1.0.0"; 
    private CRS crs = new CRS(); 
    ........ 
} 

希望這會有所幫助!

+0

與代碼一起解釋可能是有用的 –

+1

對不起,我錯過了。所以讓我再用一些更多的功能重新做整個事情。 –

0

Rashmin的答案奏效了,我找到了一種避免com.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id into a subtype of Blah問題而不需要使用registerSubtypes的替代方法。你可以做的是添加以下注釋父類:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type") 

注意的是,不同的是JsonTypeInfo.Id.CLASS,而不是JsonTypeInfo.Id.NAME。缺點是創建的JSON將包含整個類名稱,包括完整的名稱空間。好處是你不必擔心註冊子類型。

+1

這給了我這個錯誤:'引起:com.fasterxml.jackson.databind.JsonMappingException:意外的標記(END_OBJECT),預期的FIELD_NAME:缺少屬性'類型',其中包含類型id(對於類com.microstar.tap3。 contract.api.ContractObject) at [Source:org.glassfish.jersey.me[email protected]5699c6f9;行:1,列:1023](通過引用鏈:java.util.ArrayList [0])' –

2

我在使用基於dropwizard的服務時遇到了類似的問題。我不完全理解爲什麼事情對我來說並不像dropwizard代碼那樣工作,但我知道爲什麼原始文章中的代碼不起作用。 @JsonSubTypes想要一個子類型的數組,而不是一個單一的值。所以,如果你換行...

@JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) 

與...

@JsonSubTypes({ @JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) }) 

我相信你的代碼將工作。

對於那些彈出相同錯誤消息的人,您可能會遇到與正在發現的子類型有關的問題。嘗試添加類似上面的行,或者尋找發現其中包含@JsonTypeName標記的類的問題。

0

遇到相同的錯誤,並使用下面的JSON作爲Deserializer的輸入和它的工作相當於(而不是CreditCardPayment使用我的類名稱)。

{ 「類型」: 「類:CreditCardPayment」, ..... }在我來說,我已經加入到defaultImpl = SomeClass.class和@JsonTypeInfo試圖將其轉換SomeClass2.class

0