Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialize two different POJO object with the same id with Jackson

I have these two classes:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id",scope = Rol.class)
public class Rol extends MyEntity implements Serializable {
    private Integer id;
    private String rolName;

    public Rol(Integer id, String rolName) {
        this.id = id;
        this.rolName = rolName;
    }

    ...
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id",scope = User.class)
public class User extends MyEntity implements Serializable {
    private Integer id;
    private String name;
    private List<Rol> rolList;

    public User(Integer id, String name, List<Rol> rolList) {
        this.id = id;
        this.name = name;
        this.rolList = rolList;
    }

    ...
}

and I try to serialize and deserialize the user object as following

Rol rol1 = new Rol(1, "MyRol");
Rol rol2 = new Rol(1, "MyRol");
List<Rol> rolList = new ArrayList();
rolList.add(rol1);
rolList.add(rol2);

user = new User(1, "MyUser", rolList);

ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(user);
User userJson = mappe.readValue(jsonString, User.class);

and the JsonMappingException: Already had POJO for id is produced. Why?

When I review the json result of the serialization I see that the result is

{"id": 1,"name": "MyName","rolList": [{"id": 1,"rolName": "MyRol"},{"id": 1,"rolName": "MyRol"}]}

when the result should be

{"id": 1,"name": "MyName","rolList": [{"id": 1,"rolName": "MyRol"},1]}

because rol1 and rol2 are different instances of the same POJO identifier with id 1.

How can I avoid the JsonMappingException? In my project I have some different instances of the same POJO. I can guarantee that if the id's are equal -> objects are equal.

Excuse me for my bad English.

like image 305
Carlos Avatar asked Sep 11 '25 02:09

Carlos


1 Answers

For anyone returning to this question, it looks like there's option to do this with a custom ObjectIdResolver in Jackson. You can specify this on the @JsonIdentityInfo annotation, e.g. :

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name", 
    resolver = CustomObjectIdResolver.class)

Then perhaps wrap the normal SimpleObjectIdResolver class to get going and customise bindItem().

In my case I wanted to avoid overlapping objectIds, so cleared down the references when I started a new Something:

    public class CustomObjectIdResolver implements ObjectIdResolver {
        private ObjectIdResolver objectIdResolver;

        public CustomObjectIdResolver() {
            clearReferences();
        }

        @Override
        public void bindItem(IdKey id, Object pojo) {
            // Time to drop the references?
            if (pojo instanceof Something)
                clearReferences();

            objectIdResolver.bindItem(id, pojo);
        }

        @Override
        public Object resolveId(IdKey id) {
            return objectIdResolver.resolveId(id);
        }

        @Override
        public boolean canUseFor(ObjectIdResolver resolverType) {
            return resolverType.getClass() == getClass();
        }

        @Override
        public ObjectIdResolver newForDeserialization(Object context) {
            return new CustomObjectIdResolver();
        }

        private void clearReferences() {
            objectIdResolver = new SimpleObjectIdResolver();
        }
    }
like image 63
df778899 Avatar answered Sep 13 '25 07:09

df778899