Search code examples
javajsonjacksonadobe

Marshalling API response to List<POJO> with Jackson


I am having trouble parsing the response from an Adobe Campaign API endpoint into a POJO.

I am grabbing the data from the response:

String json = EntityUtils.toString(response.getEntity());

The data (heavily redacted) data looks like this:

{
  "content": [
    {
      "PKey": "@9v59tLj9c.....",
      "age": 36,
      "birthDate": "1986-04-30",
      "blackList": false,
      ...
    },
    {
      "PKey": "@9f32tLj5c.....",
      "age": 32,
      "birthDate": "1999-05-11",
      "blackList": false,
      ...
    },
    ...
  ]
}

I'm instantiating a Jackson ObjectMapper and configuring it such that the root "content" node is ignored.

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);

I have tried many different ways of parsing the data into my Profile POJO, without success. There's always an issue related to it being wrapped in "content" node, or being a list of one, or something. For brevity, the code below is for a single POJO, but I have also tried with List<Profile> since, as mentioned, the response is always a List of one or more.

// object mapper
Profile profile = objectMapper.readValue(json), Profile.class)
// ERROR: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "content" (class com.example.adobecampaignprototype.Profile), not marked as ignorable (0 known properties: ])

// object reader
ObjectReader objectReader = objectMapper.readerFor(Profile.class).withRootName("content");
Profile profile = objectReader.readValue(json);
// ERROR: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `com.example.adobecampaignprototype.Profile` from Array value (token `JsonToken.START_ARRAY`)

// array node
ArrayNode arrayNode = (ArrayNode) objectMapper.readTree(json).get("content");
// ERROR: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "content" (class com.example.adobecampaignprototype.Profile), not marked as ignorable (0 known properties: ])

// json node
JsonNode jsonNodeRoot = objectMapper.readTree(json);
JsonNode jsonNodeNested = jsonNodeRoot.get("content");
JsonNode jsonNodeActual = jsonNodeNested.get(0) // to get profile at index 0
JsonNode jsonNodeActualValue = jsonNodeActual.get("PKey") // to read single property

I've tried the above in many combinations, but have never been able to successfully parse either a Profile or List. I have read the official docs exhaustively, been through tutorials on Baeldung and elsewhere. I feel like this should be a simple thing and there's probably something obvious that I'm overlooking, but unsure what it is. Would be grateful if someone could point me toward the EASY button.


Solution

  • Probably you are overthinking about it, for example if you take a semplified version of your json input file like below as a starting point:

    {
        "content": [
            {
                "PKey": "@9v59tLj9c....."
            },
            {
                "PKey": "@9f32tLj5c....."
            }
        ]
    }
    

    You can use part of the code you wrote, so one way to deserialize it is to convert your ArrayNode array of JsonNode into a Profile[] array:

    @Data
    public class Profile {
    
        @JsonProperty("PKey")
        private String pKey;
    }
    
    ArrayNode arrayNode = (ArrayNode) mapper.readTree(json).get("content");
    Profile[] profiles = mapper.convertValue(arrayNode, Profile[].class);
    //ok it prints the array [{"PKey":"@9v59tLj9c....."},{"PKey":"@9f32tLj5c....."}]
    System.out.println(mapper.writeValueAsString(profiles));