2013-10-08 6 views
14

Ich habe das folgende Objektmodell in meinem Spring MVC (v3.2.0.RELEASE) Web-Anwendung:De-Serialisierung JSON zu polymorphen Objektmodell mit Spring und JsonTypeInfo Anmerkung

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 {} 

Wenn ich die Order-Klasse serialise zu JSON, erhalte ich folgendes Ergebnis (das ist genau das, was ich will):

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

Leider, wenn ich die oben JSON nehmen und versuchen, es zu de-serialise zurück in mein Objektmodell, erhalte ich die folgende Ausnahme:

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"])

ist meine Anwendung über Feder JavaConf konfiguriert wie folgt:

@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()); 
    } 
} 

Zum Testen habe ich einen Controller mit 2 Methoden, eine Bestellung liefert für HTTP-GET-Anforderung (dieses arbeitet) und eine, akzeptiert einen Auftrag über einen HTTP-POST (dieser schlägt fehl), z

@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) {} 

} 

Ich habe alle Vorschläge aus den verschiedenen Diskussionen anprobiert SO, aber bisher kein Glück gehabt. Kann jemand sehen, was ich falsch mache?

Antwort

8

Versuchen Subtyp mit ObjectMapper.registerSubtypes anstelle von Anmerkungen mit

+1

SomeClass2.class zu konvertieren, die gearbeitet ! Ich habe '@ JsonSubTypes.Type (name =" creditCardPayment ", Wert = CreditCardPayment.class)' von der Schnittstelle "Payment" entfernt und "objectMapper.registerSubtypes (CreditCardPayment.class);" in der Klasse "AppWebConf" hinzugefügt. Es ist enttäuschend, dass Annotationen nicht funktionierten, da ich meine Konfiguration generisch halten wollte, aber es bringt mich zumindest über dieses Problem hinaus. Vielen Dank. – grigori

+2

Ich hatte das gleiche Problem, und die obige Lösung hilft nicht. –

2

Das Verfahren zur Registrierung registerSubtypes() funktioniert!

Anmerkung1: Wir verwenden die Schnittstelle und die implementierenden Klassen. Ich möchte, dass Jackson die Klassen nach ihren implementierenden Klassen deserialisiert, Sie müssen sie alle mit der "registerSubtypes" -Methode von ObjectMapper registrieren.

Anmerkung2: Zusätzlich verwenden Sie "@JsonTypeInfo (verwenden = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property =" type ")" als Anmerkung mit Ihrer Schnittstelle.

Sie können auch die Reihenfolge der Eigenschaften definieren, wenn der Mapper einen Wert Ihres POJO als json schreibt.

Dies können Sie unter Annotation verwenden.

@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(); 
    ........ 
} 

Hoffe, das hilft!

+0

Eine Erklärung, um mit dem Code zu gehen, ist wahrscheinlich nützlich –

+1

Sorry, das habe ich verpasst. Also lass mich das Ganze nochmal mit ein paar Features nochmal machen. –

0

Rashmins Antwort funktionierte, und ich fand eine alternative Möglichkeit, das com.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id into a subtype of Blah Problem zu vermeiden, ohne registerSubtypes zu verwenden. Was Sie tun können, ist die folgende Anmerkung zu der übergeordneten Klasse hinzufügen:

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

Beachten Sie, dass die Differenz JsonTypeInfo.Id.CLASS ist statt JsonTypeInfo.Id.NAME. Der Nachteil ist, dass das erstellte JSON den gesamten Klassennamen einschließlich des vollständigen Namensraums enthält. Der Vorteil ist, dass Sie sich keine Gedanken über die Registrierung von Subtypen machen müssen.

+1

Das gibt mir diesen Fehler: 'verursacht durch: com.fasterxml.jackson.databind.JsonMappingException: Unerwartetes Token (END_OBJECT), erwarteter FIELD_NAME: fehlende Eigenschaft 'type', die type id enthalten soll (für Klasse com.microstar.tap3. contract.api.ContractObject) um [Quelle: org.glassfish.jersey.message.internal.ReaderInter[email protected]; Zeile: 1, Spalte: 1023] (durch Referenzkette: java.util.ArrayList [0]) ' –

2

Ich hatte ein ähnliches Problem bei der Arbeit an einem Dropwizard-basierten Dienst. Ich verstehe nicht ganz, warum die Dinge bei mir nicht so funktionierten wie der Dropwizard-Code, aber ich weiß, warum der Code im ursprünglichen Post nicht funktioniert. @JsonSubTypes möchte ein Array von Untertypen, nicht einen einzelnen Wert. Also, wenn Sie die Linie ersetzen ...

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

mit ...

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

Ich glaube, Ihr Code funktioniert.

Für diejenigen, die dieselbe Fehlermeldung haben, erscheint möglicherweise ein Problem mit den erkannten Subtypen. Versuchen Sie, eine Zeile wie die obige Zeile einzufügen, oder suchen Sie nach einem Problem mit der Erkennung der Klassen, in denen das Tag @JsonTypeName enthalten ist.

0

Entdeckten den gleichen Fehler und verwendet das Äquivalent von (anstelle von CreditCardPayment verwendet meinen Klassennamen) die folgenden JSON als Eingabe für Deserializer und es funktionierte.

{ "type": "CreditCardPayment", ..... }

0

In meinem Fall habe ich defaultImpl = SomeClass.class zu @JsonTypeInfo hinzugefügt hatte, und versuchte, es