Search code examples
javajsonspring-bootjacksonjson-deserialization

Use Jackson deserializer to read child JSON object


I have a Spring Boot application with Jackson dependency, and a Service with this code:

Dto dto = new ObjectMapper().readValue(jsonString, Dto.class);

I have a JSON similar to this one:

{
    "meta": {
    ...
    },
    "results": [
        {
            "id": {"raw": "1"},
            "name": {"raw": "Hello World"}
            "number": {"raw": 7.5}
        }
    ]
}

And I have a Java class like this one:

public class Dto {
    private AnotherDto meta;
    private List<ResultDto> results;
    // getters/setters
}
public class ResultDto {
    @JsonProperty("id")
    private Wrapper<String> id;

    // More fields and getters/setters
}

Then, I have a generic Wrapper class like this one (idea from Baeldung: https://www.baeldung.com/jackson-deserialization ):

@JsonDeserialize(using = WrapperDeserializer.class)
public class Wrapper<T> {
    T value;
    // getters/setters
}

Lastly, I have the next deserializer:

public class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {
    private JavaType type;

    @Override
    public Wrapper<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
        Wrapper<?> wrapper = new Wrapper<>();
        wrapper.setValue(deserializationContext.readValue(jsonParser, type));
        return wrapper;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext deserializationContext, BeanProperty beanProperty) throws JsonMappingException {
        this.type = beanProperty.getType().containedType(0);
        return this;
    }
}

The main doubt is, how I can access to jsonParser object in order to get the child JSON information? In this case, I would like to access {"raw": "1"}, so I could get the child into raw key and get the proper value, so ID "1" would be saved in final Java object.

I wouldn't want to make deserializationContext.readValue(jsonParser, type) as in this example, because it would throw this error:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: 

Because {"raw": "1"} is not a valid String. I would want to have only the "1" value.


Solution

  • You are really close. The main issue is that you used the field name value where it should be raw. Also, the WrapperDeserializer is not required.

    All the code is as you have it besides the following:

    Wrapper.java

    public class Wrapper<T> {
    
        T raw;
      
        // getters, setters & toString()
    }
    

    main

        public static void main(String[] args) throws JsonMappingException, JsonProcessingException {
        
            ObjectMapper objectMapper = new ObjectMapper();
            
            String json = "{\r\n" + 
                    "    \"results\": [\r\n" + 
                    "        {\r\n" + 
                    "            \"id\": {\"raw\": \"1\"},\r\n" + 
                    "            \"name\": {\"raw\": \"Hello World\"},\r\n" + 
                    "            \"number\": {\"raw\": 7.5}\r\n" + 
                    "        }\r\n" + 
                    "    ]\r\n" + 
                    "}";
            
            Dto readValue = objectMapper.readValue(json, Dto.class);
            
            System.out.println(readValue);
    }
    

    Output

    Dto [results=[ResultDto [id=Wrapper [raw=1], name=Wrapper [raw=Hello World], number=Wrapper [raw=7]]]]