I’m new to Java, and I’m wondering how to deserialize an empty JSON array into an empty Java object of type TreeMap<String, MyOtherClass>
.
Currently, I’m attempting to deserialize a JSON file with an array of objects, and each object into a class called MyClass
. The class is roughly as follows:
@JsonIgnoreProperties(ignoreUnknown = true) public class MyClass { private final String propertyOne; private final String propertyTwo; private final String propertyThree; @JsonSerialize(contentAs = MyOtherClass.class) @JsonDeserialize(contentAs = MyOtherClass.class) TreeMap<String, MyOtherClass> otherThings = new TreeMap<>(); @JsonCreator public MyClass( @JsonProperty("propertyOne") String propertyOne, @JsonProperty("propertyTwo") String propertyTwo, @JsonProperty("propertyThree") String propertyThree) { this.propertyOne = propertyOne; this.propertyTwo = propertyTwo; this.propertyThree = propertyThree; // Getters and setters below @JsonSetter("otherThings") public void setOtherThings() { if (this.otherThings == null || this.otherThings.isEmpty()) { this.otherThings = new TreeMap<>(); } } } }
One of the entries in the original JSON is this field otherThings
. I’ve represented this entry using MyOtherClass
, which handles the properties that otherThings
could contain.
Currently, this works perfect for serialization, and for deserialization when otherThings
is populated. However, when I have something like the following:
{ "propertyOne": "This is propertyOne!", "propertyTwo": "This is propertyTwo!", "propertyThree": "This is propertyThree!", "otherThings": [] }
I get the following stack trace:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.TreeMap<java.lang.String,org.something.model.MyOtherClass>` from Array value (token `JsonToken.START_ARRAY`) at [Source: (URL); line: 7, column: 14] (through reference chain: java.util.ArrayList[0]->org.something.model.Order["lines"]) at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1741) at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1515) at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromArray(StdDeserializer.java:222) at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:447) at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:32) at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:277) at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:462) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:351) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:355) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28) at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4675) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3572) at org.something.model.MyClass.populateMyClassFromJson(MyClass.java:148) at org.something.service.MyClassService.displayMyClassObject(MyClassService.java:96) at Main.main(Main.java:9)
I’ve tried:
- Using
ObjectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)
, but that doesn’t really help sinceotherThings
gets instantiated asnull
. - Using the
JsonSetter
forsetOtherThings
you see above. This has to be careful because I don’t want to overwriteotherThings
if it already contains things inside of it.
EDIT: Here’s also the method populateMyClassFromJson
if it helps:
public void populateMyClassFromJson() throws IOException { List<MyClass> myClassList = mapper.readValue(new File("/path/to/my_classes.json"), new TypeReference<>() {}); Map<String, MyClass> myClassMap = myClassList.stream().collect(Collectors.toMap(MyClass::getId, Function.identity())); myClassTreeMap.putAll(myClassMap); }
Advertisement
Answer
Just to summarize (I believe it was clear from the start): as error states the problem is that Jackson tries to deserialize field as TreeMap which is not going to work since the in JSON there is an array []
.
However your setter seems a bit odd to me. I believe you should have something like this (updated based on your comment and update):
@JsonSetter("otherThings") public void setOtherThings(MyOtherClass[] arr) { this.otherThings = List.of(Optional.ofNullable(arr) .orElse(new MyOtherClass[0])).stream() .collect(Collectors.toMap(MyOtherClass::getId, Function.identity(), (o1, o2) -> o1, TreeMap::new)); }
Of course you should also handle the case when the array is not empty.