levant pied
levant pied

Reputation: 4501

Finer control over jackson2 deserialization

I have this class:

class Address {
  int id;
  String address;
  // ... other fields
}

I have many other classes that embed Address, one example:

class Person {
  int id;
  String name;
  Address address;
  // ... other fields
}

I receive a json representing a person such as:

{
  "id": 10,
  "name": "John",
  "addressId": 50,
  // ... other fields
}

I can deserialize the above into

class PersonJson {
  int id;
  String name;
  int addressId;
  // ... other fields
}

and then convert PersonJson into Person.

However, this can happen deeper in the hierarchy. I have Organization that has a List<Person>, so I'd need to have OrganizationJson and List<PersonJson> that I then need to convert to Organization and List<Person>.

Alternatively, I can make a custom deserializer for Person, but since Address is embedded in many other objects I'd have to make a deserializer for each of these. I don't think I can reuse much of this and most other fields would be deserialized by default (i.e. primitives, etc.) by ObjectMapper.

Both are doable, but both are a duplication of the work ObjectMapper already does and require maintenance in multiple places, which is a pain. Is there a way to configure ObjectMapper to custom-deserialize a field (including within a collection such as List<Address>) or some other way to avoid duplication?

Upvotes: 0

Views: 89

Answers (1)

bleidi
bleidi

Reputation: 100

The first atempt that occour me is to combine the @JsonProperty with @JsonCreator annotations to instruct de object mapper to address (no pun intended) the deserialize to the correct nested object.

For sake of simplicity, and knowing your immutablility by you comment, there is an example below using lombok.

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;

import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;

public class JsonTest {

    @Data
    @Builder(toBuilder = true)
    @ToString
    @Getter
    static class Address {
        int id;
        @NonNull
        String address;

        @JsonCreator
        public static Address factory(int id) {
            return new AddressBuilder().id(id).address("getAddressLogicHere").build();
        }
    }

    @Data
    @JsonDeserialize(builder = Person.PersonBuilder.class)
    @Builder(toBuilder = true)
    @ToString
    @Getter
    static class Person {
        private final int id;
        @NonNull
        private final String name;
        @JsonProperty("addressId")
        private final Address address;

        @JsonPOJOBuilder(withPrefix = "")
        public static class PersonBuilder {
        }
    }

    public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
        String json = "{\n" + 
                "  \"id\": 10,\n" + 
                "  \"name\": \"John\",\n" + 
                "  \"addressId\": 50\n" + 
                "}";
        Person person = new ObjectMapper().readValue(json, Person.class);
        System.out.println(person);
        //JsonTest.Person(id=10, name=John, address=JsonTest.Address(id=50, address=getAddressLogicHere))
    }

}

Upvotes: 1

Related Questions