Ahmad.Masood
Ahmad.Masood

Reputation: 1309

jackson: mapping some fields of json to inner fields of class

I want to map some fields of json to inner fields of a class. e.g

{
 values:[{
 "name":"Abc",
 "age":18,
 "street":"test", 
 "postalcoad":"1231412"
},
 {
  "name":"ccvb",
 "age":20,
 "street":"test2", 
 "postalcoad":"123"
  }
]}

Following i my java class

@JsonIgnoreProperties(ignoreUnknown = true)
public class Customer{
   @JsonProperty("name")
   private string name;

   @JsonProperty("age")
   private int age;

   private Address address;
}

@JsonIgnoreProperties(ignoreUnknown = true)
public class Address{
   @JsonProperty("street")
   private string street;

   @JsonProperty("postalcode")
   private string postalcode;
}


ObjectMapper mapper = new ObjectMapper();
Customer[] c = mapper.readValue(mapper.readTree(json).get("values").toString(), Customer[].class);

It returns me Customer object without Address. Any idea how can i create Address object from this json.

Upvotes: 3

Views: 5365

Answers (2)

AllanT
AllanT

Reputation: 961

I would create a custom deserializer and inside of it call the default deserializer for Customer and then call the default deseriazlier for Address. Then you add the address to the customer object. This way they both look at the same json but you get two different objects out and you can connect them the way you want.

To call a standard deserializer from a custom deseriazlier see this answer: How do I call the default deserializer from a custom deserializer in Jackson.

Upvotes: 1

Vlad Bochenin
Vlad Bochenin

Reputation: 3082

One of the options is to use @JsonCreator annotation:

    @JsonCreator
    public Customer(
            @JsonProperty("name") String name,
            @JsonProperty("age")  int age,
            @JsonProperty("street") String street,
            @JsonProperty("postalcode")   String postalcode
    ) {
        this.name = name;
        this.age = age;
        this.address = new Address();
        this.address.street = street;
        this.address.postalcode = postalcode;
    }

Second option is create custom deserializer and bind your class with deserializer using @JsonDeserialize annotation

@JsonDeserialize(using = CustomerDeserializer.class)
public static class Customer{
  ....
}

public class CustomerDeserializer extends StdDeserializer<Customer> {

    public CustomerDeserializer() {
        super(Customer.class);
    }

    @Override
    public Customer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        Customer customer = new Customer();
        JsonNode treeNode = p.readValueAsTree();
        if (treeNode == null) {
            return null;
        }
        customer.setName(treeNode.get("name").asText());
        customer.setAge(treeNode.get("age").asInt());
        Address address = new Address();
        address.setStreet(treeNode.get("street").asText());
        address.setPostalcode(treeNode.get("postalcode").asText());
        customer.setAddress(address);
        return customer;
    }
}

As third option, you can use @JsonAnySetter with some kind of post construct processing:

public interface PostConstruct {
    void postConstruct();
}

public class Customer implements PostConstruct {
    //mapping 

    private Map<String, Object> additionalFields = new HashMap<>();

    @JsonAnySetter
    public void setAdditionalValue(String key, Object value) {
        additionalFields.put(key, value);
    }

    @Override
    public void postConstruct() {
        address = new Address();
        address.setStreet(String.valueOf(additionalFields.get("street")));
        address.setPostalcode(String.valueOf(additionalFields.get("postalcode")));
    }
}


public static class PostConstructDeserializer extends DelegatingDeserializer {
    private final JsonDeserializer<?> deserializer;

    public PostConstructDeserializer(JsonDeserializer<?> deserializer) {
        super(deserializer);
        this.deserializer = deserializer;
    }

    @Override
    protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
        return deserializer;
    }

    @Override
    public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        Object result = _delegatee.deserialize(jp, ctxt);
        if (result instanceof PostConstruct) {
            ((PostConstruct) result).postConstruct();
        }
        return result;
    }
}


//using of post construct deserializer

    ObjectMapper mapper = new ObjectMapper();
    SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier() {
        @Override
        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                                                      BeanDescription beanDesc,
                                                      final JsonDeserializer<?> deserializer) {
            return new PostConstructDeserializer(deserializer);
        }
    });
    mapper.registerModule(module);

Upvotes: 4

Related Questions