I have the following object model in my Spring MVC (v3.2.0.RELEASE) web application:
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 {}
When I serialise the Order class to JSON, I get the following result (which is exactly what I want):
{ "payment" : { "creditCardPayment": { ... } }
Unfortunately, if I take the above JSON and try to de-serialise it back into my object model, I get the following exception:
Could not read JSON: Could not resolve type id 'creditCardPayment' into a subtype of [simple type, class Payment] at [Source: org.apache.catalina.connector.CoyoteInputStream@19629355; 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: org.apache.catalina.connector.CoyoteInputStream@19629355; line: 1, column: 58] (through reference chain: Order["payment"])
My application is configured via Spring JavaConf, as follows:
@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()); } }
For testing, I have a controller with 2 methods, one returns an Order for HTTP GET request (this one works) and one that accepts an Order via a HTTP POST (this one fails), e.g.
@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) {} }
I have tried all suggestions from the various discussions on SO but so far had no luck. Can anyone spot what I'm doing wrong?
When using Newtonsoft’s libraries, one would add support for polymorphic deserialization by storing type information for a JSON object in a property named $type. Technically, the data could have us instantiating whatever type from whatever assembly it wanted!
In cases where polymorphic types are persisted to JSON, there's no way for Jackson to figure out the right type during deserialization. Let's understand that with an example. public class Rectangle extends Shape { private int w; private int h; .............
This new class is going to itself be a base class that will require a deriving class to get any use out of it with specific datasets. This new mystery class shall heretofore be known as the JsonPolymorphicConverter<TTypeDescriptor,TBase> class.
@JsonTypeInfo annotation can be used both on classes (above example) and properties. In our example using the annotation on 'shapes' property: If this annotation exists on both class and property, then the one on property has precedence, as it is considered more specific.
Try to register subtype using ObjectMapper.registerSubtypes
instead of using annotations
The method registerSubtypes()
works!
@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(); }
Note1: We use the Interface and the implementing classes. I fyou want jackson to de-serialize the classes as per their implementing classes, you have to register all of them using ObjectMapper's "registerSubtypes" method.
Note2: In addition you use, " @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")" as annotation with your Interface.
You can also define the order of properties when mapper writes a value of your POJO as a json.
This you can do using below annotation.
@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(); ........ }
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With