How can I deserialize a nested wrapped String in Jackson?
Asked Answered
C

2

8

I have a JSON string that contains a nested and wrapped JSON string. I'd like to deserialize this using Jackson but I'm unsure how. Here's a sample bean:

@JsonIgnoreProperties(ignoreUnknown = true)
public class SomePerson {

    public final String ssn;
    public final String birthday;
    public final Address address;

    @JsonCreator
    public SomePerson(
            @JsonProperty("ssn") String ssn,
            @JsonProperty("birthday") String birthday,
            @JsonProperty("data") Address address,
            @JsonProperty("related") List<String> related) {
        this.ssn = ssn;
        this.birthday = birthday;
        this.address = address;
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Address {

        public final String city;
        public final String country;

        @JsonCreator
        public Address(
                @JsonProperty("city") String city,
                @JsonProperty("country") String country) {
            this.city = city;
            this.country = country;
        }
    }
}

The JSON string resembles this:

{
  ssn: "001",
  birthday: "01.01.2020",
  address: "{ city: \"London\", country: \"UK\" }"
}

While I've deserialized nsted objects before - I'm rather lost as to how to do this when the object is a wrapped string.

Caddric answered 13/2, 2021 at 11:40 Comment(3)
Why is "address" a String in your model? It should be an instance of the Address type. – Unite
Sorry, my bad. It was a typo. πŸ™πŸΌ – Caddric
Have you seen my answer? Take a look also at: Is it possible to make Jackson serialize a nested object as a string – Pakistan
K
8

When internal object is escaped JSON String we need to deserialise it "twice". First time is run when root JSON Object is deserialised and second time we need to run manually. To do that we need to implement custom deserialiser which implements com.fasterxml.jackson.databind.deser.ContextualDeserializer interface. It could look like this:

class FromStringJsonDeserializer<T> extends StdDeserializer<T> implements ContextualDeserializer {

    /**
     * Required by library to instantiate base instance
     * */
    public FromStringJsonDeserializer() {
        super(Object.class);
    }

    public FromStringJsonDeserializer(JavaType type) {
        super(type);
    }

    @Override
    public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = p.getValueAsString();

        return ((ObjectMapper) p.getCodec()).readValue(value, _valueType);
    }


    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        return new FromStringJsonDeserializer<>(property.getType());
    }
}

We need to annotate our property with this class:

@JsonDeserialize(using = FromStringJsonDeserializer.class)
public final Address address;

Since now, you should be able to deserialise above JSON payload to your POJO model.

See also:

Keeney answered 13/2, 2021 at 18:43 Comment(1)
Very detailed explanation. – Chadchadabe
L
2

readValue(String,Class) should work:

Address addObject = mapper.readValue(root.getString("address"), Address.class);

Where root is your main JSONObject.

Letishaletitia answered 13/2, 2021 at 12:2 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.