Home > Software design >  Deserialize empty JSON array into empty TreeMap
Deserialize empty JSON array into empty TreeMap

Time:12-06

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 since otherThings gets instantiated as null.
  • Using the JsonSetter for setOtherThings you see above. This has to be careful because I don't want to overwrite otherThings 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);
}

CodePudding user response:

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.

  • Related