Gili Gili - 4 months ago 33
JSON Question

Jackson + Builder Pattern?

I'd like Jackson to deserialize a class with the following constructor:

public Clinic(String name, Address address)


Deserializing the first argument is easy. The problem is that Address is defined as:

public class Address {
private Address(Map<LocationType, String> components)
...

public static class Builder {
public Builder setCity(String value);
public Builder setCountry(String value);
public Address create();
}
}


and is constructed like this:
new Address.Builder().setCity("foo").setCountry("bar").create();


Is there a way to get key-value pairs from Jackson in order to construct the Address myself? Alternatively, is there a way to get Jackson to use the Builder class itself?

Answer

I ended up implementing this using the @JsonDeserialize as follows:

@JsonDeserialize(using = JacksonDeserializer.class)
public class Address
{...}

@JsonCachable
static class JacksonDeserializer extends JsonDeserializer<Address>
{
    @Override
    public Address deserialize(JsonParser parser, DeserializationContext context)
        throws IOException, JsonProcessingException
    {
        JsonToken token = parser.getCurrentToken();
        if (token != JsonToken.START_OBJECT)
        {
            throw new JsonMappingException("Expected START_OBJECT: " + token, parser.getCurrentLocation());
        }
        token = parser.nextToken();
        Builder result = new Builder();
        while (token != JsonToken.END_OBJECT)
        {
            if (token != JsonToken.FIELD_NAME)
            {
                throw new JsonMappingException("Expected FIELD_NAME: " + token, parser.getCurrentLocation());
            }
            LocationType key = LocationType.valueOf(parser.getText());

            token = parser.nextToken();
            if (token != JsonToken.VALUE_STRING)
            {
                throw new JsonMappingException("Expected VALUE_STRING: " + token, parser.getCurrentLocation());
            }
            String value = parser.getText();

            // Our Builder allows passing key-value pairs
            // alongside the normal setter methods.
            result.put(key, value);
            token = parser.nextToken();
        }
        return result.create();
    }
}