Home > Enterprise >  Jackson JSON chokes when deserializing a Collection
Jackson JSON chokes when deserializing a Collection

Time:02-24

Java 11, Spring Boot 2.5 and Jackson 2.12 here. I have the following Java classes:

// using lombok annos to generate getters & setters
@Data
public class ExamUnit implements Comparable<ExamUnit> {

    private Long id;
    private String name;
    private String displayName;
    private Long order;
    private Exam exam;

    // compareTo, equals & hashCode are necessary so that the TreeSet below sorts ExamUnits
    // correctly, according to my needs
    @Override
    public int compareTo(ExamUnit other) {
        if (this.equals(other)) {
            return 0;
        } else if (this.order < other.order) {
            return 1;
        }

        return 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ExamUnit examUnit = (ExamUnit) o;
        return Objects.equals(name, examUnit.name) &&
                Objects.equals(displayName, examUnit.displayName) &&
                Objects.equals(order, examUnit.order);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, displayName, order);
    }
}

and:

@Data
public class Exam {

    private Long id;
    private String name;
    private String displayName;
    private SortedSet<ExamUnit> units = new TreeSet<>();

}

Then I have an "exam" JSON string:

{
  "id": 1,
  "name": "science",
  "displayName": "Science Exam",
  "units": [
    {
      "id": 1,
      "name": "chemistry",
      "displayName": "Chemistry Unit",
      "order": 1
    },
    {
      "id": 2,
      "name": "biology",
      "displayName": "Biology Unit",
      "order": 2
    }
  ]
}

This is valid JSON, as you can verify yourself. And it does appear to follow the model of the Java types correctly.

Then I have some code to read the string and deserialize it:

@Test
public void deserializeExamJson() {

    String examJson = "{\n"  
            "  \"id\": 1,\n"  
            "  \"name\": \"science\",\n"  
            "  \"displayName\": \"Science Exam\",\n"  
            "  \"units\": [\n"  
            "    {\n"  
            "      \"id\": 1,\n"  
            "      \"name\": \"chemistry\",\n"  
            "      \"displayName\": \"Chemistry Unit\",\n"  
            "      \"order\": 1\n"  
            "    },\n"  
            "    {\n"  
            "      \"id\": 2,\n"  
            "      \"name\": \"biology\",\n"  
            "      \"displayName\": \"Biology Unit\",\n"  
            "      \"order\": 2\n"  
            "    }\n"  
            "  ]\n"  
            "}";

    Exam exam = null;
    try {
        exam = objectMapper.readValue(examJson, Exam.class);
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }

    assertThat(exam.getName()).isEqualTo("science");

}

When this test runs, it fails with:

java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.util.SortedSet)
 at [Source: (StringReader); line: 6, column: 5] (through reference chain: com.me.myapp.Exam["units"])

CodePudding user response:

In your ExamUnit class annotate your property private Exam exam; with @JsonIgnore

  • Related