Deserialize JSON object / map as a generic set with Jackson

I have JSON cards:

"things": {"foo": {"name": "foo", ...}, "bar": {"name": "bar", ...}} 

I want to deserialize them as if they were arrays:

 "things": [{"name": "foo", ...}, {"name": "bar", ...}] 

(according to XML / JAXB deserialization behavior):

 <things><thing name="foo">...</thing><thing name="bar">...</thing></things> 

in a collection such as:

 @XmlElementWrapper @XmlElement(name = "thing") @JsonDeserialize(using = MapToCollectionDeserializer.class) Collection<Thing> things; 

Note that I have collections with different types of elements, not just Thing - so I need a common mechanism.

However, when writing a custom deserializer, what is the right way to access context type information?

 public class MapToCollectionDeserializer extends StdDeserializer<Object> { @Override public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { Preconditions.checkState(jp.getCurrentToken() == JsonToken.START_OBJECT); final LinkedList<Object> result = new LinkedList<>(); JsonToken tok; while ((tok = jp.nextToken()) != JsonToken.END_OBJECT) { Preconditions.checkState(tok == JsonToken.FIELD_NAME); // How to get the collection element type for deserialization? result.add(...); } return result; } } 

My approach still uses the ContextualDeserializer , which can provide BeanProperty (which contains type information) for the deserializer. However, the JsonDeserializer should still have a no-arg constructor, so I first create a broken object:

 public class MapToCollectionDeserializer extends StdDeserializer<Object> implements ContextualDeserializer<Object> { private final BeanProperty property; public MapToCollectionDeserializer() { super(Collection.class); property = null; // YUCK: BROKEN!!! } private MapToCollectionDeserializer(BeanProperty property) { super(property.getType()); this.property = property; } @Override public JsonDeserializer<Object> createContextual(DeserializationConfig config, BeanProperty property) throws JsonMappingException { return new MapToCollectionDeserializer(property); } @Override public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { Preconditions.checkState(jp.getCurrentToken() == JsonToken.START_OBJECT); final JavaType elementType = property.getType().containedType(0); final LinkedList<Object> result = new LinkedList<>(); JsonToken tok; while ((tok = jp.nextToken()) != JsonToken.END_OBJECT) { Preconditions.checkState(tok == JsonToken.FIELD_NAME); jp.nextToken(); final JsonDeserializer<Object> valueDeser = ctxt.getDeserializerProvider() .findValueDeserializer(ctxt.getConfig(), elementType, property); result.add(valueDeser.deserialize(jp, ctxt)); } return result; } } 

Is there a better / easier way to do this?

+4
source share
1 answer

It sounds like you have stopped using Jackson, but for those who have a similar problem, you can enable DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY . If this option is turned on when Jackson finds the object in JSON, but it must be deserialized in the collection, he will create a collection and put the object in a collection that looks like what you want here.

-1
source

Source: https://habr.com/ru/post/1404035/


All Articles