0

I understand that I can create a map of a specified object with Jackson by using TypeReference. For instance, taking a class ...

public class Error {    
    /** Error code */
    private final String errcode;
    /** Error message */
    private final String error; 
// Getter
}

... and the data in JSON

{
   "firstError":{
      "errcode":"1234",
      "error":"The 1. message"
   },
   "secondError":{
      "errcode":"5678",
      "error":"The 2. message"
   }
}

... I can deserialize with

TypeReference<HashMap<String, Error>> typeRef  = new TypeReference<HashMap<String, Error>>() {};
Map<String, Error> map = new ObjectMapper().readValue(jsonInput, typeRef);

My question is now: What can I do if my JSON looks like this?

{
   "date":"2022-01-01",
   "server":"myfancyserver",
   "errors":{
      "firstError":{
         "errcode":"1234",
         "error":"The 1. message"
      },
      "secondError":{
         "errcode":"5678",
         "error":"The 2. message"
      }
   }
}
felix_w
  • 15
  • 7
  • 1
    Does this answer your question? [Java JSON -Jackson- Nested Elements](https://stackoverflow.com/questions/8388656/java-json-jackson-nested-elements) – pringi Feb 21 '22 at 14:41

1 Answers1

1

First, in order to deserialize the nested Error objects containing final fields, it is needed to mark the all-args constructor with @JCreator annotation and use @JsonProperty to set the values properly:

public class Error {
    private final String errcode;
    private final String error;

    @JsonCreator
    public Error(@JsonProperty("errcode") String errcode, @JsonProperty("error") String error) {
        this.errcode = errcode;
        this.error = error;
    }
    // ... getters/toString, etc.
}

Then ObjectMapper::convertValue should be used to read the contents of the map from the JsonNode:

String json = ...; // input JSON string

ObjectMapper om = new ObjectMapper();

JsonNode node = om.readTree(json); // throws JsonProcessingException 

Map<String, Error> errors = om.convertValue(
    node.get("errors"), new TypeReference<Map<String, Error>>(){}
);

errors.forEach((k, v) -> System.out.println(k + " = " + v));
// -> firstError = {errcode='1234', error='The 1. message'}
// -> secondError = {errcode='5678', error='The 2. message'}
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42
  • Thanks! (And sorry for adding confusion regard the final fields, I am about to refactor the code). – felix_w Feb 21 '22 at 20:37
  • Yes, if the fields are not final, Jackson would be able to deal with default no-args constructor and setters. – Nowhere Man Feb 21 '22 at 20:38
  • One final thing to this solution: What would be the best solution to get the other data in the JSON into a POJO? Remove the parts which are read with a om.convertValue and then call readValue(jsonString, MySurroundingPOJO.class)? – felix_w Feb 21 '22 at 20:44
  • If other parts of that JSON need to be read into POJO, it could make sense to read as many fields as needed and ignore "unknown" properties by supplying `@JsonIgnoreProperties(ignoreUnknown = true)` to that POJO class. – Nowhere Man Feb 21 '22 at 21:55