skip to Main Content

This is a follow up question of an answer to Jackson custom getters.

The solution works and creates proper JSON. The problem is deserialization. I have an unwrapped object with some attributes not shown in JSON but instead replaced by a new attribute (e.g. firstname+surname => name).

This is correctly serialized to

{"age":22,"name":"Alex Doe"}

This class can not be edited, so we rely on mixin.

The problem on deserialization is, that PersonSerializer.setFullname() is called before PersonSerializer.setPerson(), probably because attributes are deserialized before subobjects. Which forces me to have a Person-object created when PersonSerializer is created by the ObjectMapper. This Person-object is overwritten when age is deserialized with an Person-object only containing the age:

firstName: null, surname: null, age: 22

I could store fullname in setFullname() and fill it into person when setPerson() is called but this feels incorrect.

What is the proper way to solve this problem?

Code:

public final class Person {

    private String firstName;
    private String surname;
    private int age;

    public Person() {
        super();
    }

    Person(final String firstName, final String surname, final int age) {
        super();
        this.firstName = firstName;
        this.surname = surname;
        this.age = age;
    }
 //getter and setter
}

Serialization helper and mixin based on the answer:

public class PersonSerializer {

    private Person person;

    public PersonSerializer() {
        super();
        person = new Person(); //needed, otherwise NPE in setFullname()
    }

    @JsonUnwrapped
    Person getPerson() {
        return person;
    }

    //customer getter to join firstname and surname
    @JsonGetter("name")
    String fullName() {
        return person.getFirstName() + " " + person.getSurname();
    }

    @JsonSetter("name")
    public void setFullname(final String fullName) {
        final String[] s = fullName.split(" ");
        person.setFirstName(s[0]);
        person.setSurname(s[1]);
    }

    public void setPerson(Person p) {
        person = p;
    }
}

and

abstract class PersonMixin {

    @JsonIgnore String firstName;
    @JsonIgnore String surname;
}

Testcode:

public static void main(String[] args)

        throws JsonProcessingException {

        final ObjectMapper mapper = new ObjectMapper().addMixIn(Person.class, PersonMixin.class);
        mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY);
        final PersonSerializer alex = new PersonSerializer();
        alex.setPerson(new Person("Alex", "Doe", 22));
        final String s = mapper.writeValueAsString(alex);
        out.println(s);

        final Person newAlex = mapper.readValue(s, PersonSerializer.class).getPerson();
        out.println(String.format(
            "firstName: %s, surname: %s, age: %d",
            newAlex.getFirstName(),
            newAlex.getSurname(),
            newAlex.getAge()
        ));
    }

I searched for examples, documentation and StackOverflow but to no avail.

2

Answers


  1. You can try this solution, it works fine:
    You only need the person calss to get that you want

    public final class Person {
    
        private String firstName;
        private String surname;
        private int age;
    
        public Person() {
            super();
        }
    
        Person(final String firstName, final String surname, final int age) {
            super();
            this.firstName = firstName;
            this.surname = surname;
            this.age = age;
        }
     //getter and setter
    
    
    
    
    
        public static void main(String[] args) throws JsonProcessingException {
    
            final ObjectMapper mapper = new ObjectMapper();
            final Person alex = new Person("Alex", "Doe", 22);
            final String s = mapper.writeValueAsString(alex);
            System.out.println(s);
    
            final Person newAlex = mapper.readValue(s, Person.class);
    
            System.out.println(String.format(
                    "firstName: %s, surname: %s, age: %d",
                    newAlex.getFirstName(),
                    newAlex.getSurname(),
                    newAlex.getAge()
            ));
        }
    }
    

    OUTPUT is :

    {"firstName":"Alex","surname":"Doe","age":22}
    firstName: Alex, surname: Doe, age: 22
    

    Let me know

    Login or Signup to reply.
  2. I think your code can be more simple:
    What you need is a Wrapper of Person class since you can’t edit it.

    Person:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Person {
    
        private String firstName;
        
        private String surname;
        
        private int age;
    
    }
    

    Person Wrapper

    public class PersonWrapper extends Person {
         
        private String name;
        
        public PersonWrapper() {
            super();
        }
    
        public PersonWrapper(String firstName, String surname, int age) {
            super(firstName, surname, age);
            name = getName();
        }
    
        public String getName() {
            this.name = getFirstName() + " " + getSurname();
            return name;
        }
        
        public void setName(final String name) {
            final String[] s = name.split(" ");
            setFirstName(s[0]);
            setSurname(s[1]);
        }
        
    }
    

    PersonMixIn:

    public abstract class PersonMixIn {
    
        @JsonIgnore 
        String firstName;
        
        @JsonIgnore 
        String surname;
        
    }
    

    TEST:

    public class Application {
    
        public static void main(String[] args) throws JsonProcessingException {
            
            final ObjectMapper mapper = new ObjectMapper().addMixIn(PersonWrapper.class, PersonMixIn.class);
            final PersonWrapper alex = new PersonWrapper("Alex", "Doe", 22);
            final String s = mapper.writeValueAsString(alex);
            System.out.println(s);
    
            final Person newAlex = mapper.readValue(s, PersonWrapper.class);
            System.out.println(String.format(
                "firstName: %s, surname: %s, age: %d",
                newAlex.getFirstName(),
                newAlex.getSurname(),
                newAlex.getAge()
            ));
            
        }
    
    }
    

    result:

    Serialization: {"age":22,"name":"Alex Doe"}

    Deserialization: firstName: Alex, surname: Doe, age: 22

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search