Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save varren/ba5efcaac434e8ec5a291e26a5c380ae to your computer and use it in GitHub Desktop.
Save varren/ba5efcaac434e8ec5a291e26a5c380ae to your computer and use it in GitHub Desktop.
Jackson @JsonIdentityInfo with always embedded whole objects during serialization
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonIdentityInfoSerialization {
/**************************************************************************
******************************* POJOS *************************************
***************************************************************************/
/******************************* Contact.java ****************************/
public static class Contact {
@JsonProperty("id")
private int id;
@JsonIdentityReference(alwaysAsId = true)
@JsonProperty("phones")
private List<Phone> phones;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
}
/******************************* Phone.java ****************************/
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
resolver = MyObjectIdResolver.class,
property = "id", scope = Phone.class)
public static class Phone {
@JsonProperty("id")
private int id;
@JsonProperty("n")
private String n;
public Phone() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getN() {
return n;
}
public void setN(String n) {
this.n = n;
}
}
/**************************************************************************
******************************* OBJECT MAPPERS ****************************
***************************************************************************/
public static ObjectMapper defaultObjectMapper() {
return new ObjectMapper();
}
/****** this mapper ignores @JsonIdentityInfo during serialization **********/
public static ObjectMapper ignoreJsonIdentityInfoMapper() {
ObjectMapper m = new ObjectMapper();
SerializationConfig config = m.getSerializationConfig()
.with(new JacksonAnnotationIntrospector() {
@Override
public ObjectIdInfo findObjectIdInfo(final Annotated ann) {
return null;
}
});
m.setConfig(config);
return m;
}
/** this mapper ignores @JsonIdentityReference(alwaysAsId = true) during serialization **/
public static ObjectMapper ignoreJsonIdentityReferenceMapper() {
ObjectMapper m = new ObjectMapper();
SerializationConfig config2 = m.getSerializationConfig()
.with(new JacksonAnnotationIntrospector() {
@Override
public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) {
return null;
}
});
m.setConfig(config2);
return m;
}
/**************************************************************************
*******************************TESTS **************************************
***************************************************************************/
public static void main(String[] args) throws Exception {
// init data
Phone p1 = new Phone();
p1.setId(3);
p1.setN("b");
Phone p2 = new Phone();
p2.setId(2);
p2.setN("a");
List<Phone> phones = new ArrayList<>();
phones.add(p1);
phones.add(p2);
phones.add(p1); // add p1 2 times
Contact contact = new Contact();
contact.setPhones(phones);
contact.setId(1);
ArrayList<Contact> contacts = new ArrayList<>();
contacts.add(contact); // add same contact 2 times
contacts.add(contact);
System.out.println("\nObjectMapper1 without customisations");
fullTest(defaultObjectMapper(), contacts);
System.out.println("\nObjectMapper2 with ignoreJsonIdentityInfoMapper");
fullTest(ignoreJsonIdentityInfoMapper(), contacts);
System.out.println("\nObjectMapper3 with ignoreJsonIdentityReferenceMapper");
fullTest(ignoreJsonIdentityReferenceMapper(), contacts);
}
private static void fullTest(ObjectMapper mapper, ArrayList<Contact> testData) throws IOException {
System.out.println("Serialization tests:");
System.out.println("Contact : " + mapper.writeValueAsString(testData.get(0)));
System.out.println("List<Contact>: " + mapper.writeValueAsString(testData));
System.out.println("Deserialization tests:");
deserializationTest(mapper, "{\"id\":1,\"phones\":[{\"id\":3,\"n\":\"a\"},{\"id\":2,\"n\":null},3]}");
deserializationTest(mapper, "{\"id\":1,\"phones\":[{\"id\":3,\"n\":\"b\"},{\"id\":2,\"n\":null},{\"id\":3,\"n\":\"b\"}]}");
deserializationTest(mapper, "{\"id\":1,\"phones\":[1,2,1]}");
}
private static void deserializationTest(ObjectMapper mapper, String json) throws IOException {
System.out.println("From: " + json);
Contact result = mapper.readValue(json, Contact.class);
System.out.println("To : " + mapper.writeValueAsString(result));
}
/**************************************************************************
**************** ObjectIdResolver used for deserialization ****************
***************************************************************************/
private static class MyObjectIdResolver implements ObjectIdResolver {
private Map<ObjectIdGenerator.IdKey, Object> _items = new HashMap<>();
@Override
public void bindItem(ObjectIdGenerator.IdKey id, Object pojo) {
if (!_items.containsKey(id)) _items.put(id, pojo);
}
@Override
public Object resolveId(ObjectIdGenerator.IdKey id) {
Object object = _items.get(id);
if (object != null) return object;
try {
object = id.scope.getConstructor().newInstance(); // create instance
id.scope.getMethod("setId", int.class).invoke(object, id.key); // set id
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
@Override
public ObjectIdResolver newForDeserialization(Object context) {
return new MyObjectIdResolver();
}
@Override
public boolean canUseFor(ObjectIdResolver resolverType) {
return resolverType.getClass() == getClass();
}
}
@varren
Copy link
Author

varren commented Oct 7, 2017

OUTPUT:

ObjectMapper1 without customisations
Serialization tests:
Contact : {"id":1,"phones":[3,2,3]}
List: [{"id":1,"phones":[3,2,3]},{"id":1,"phones":[3,2,3]}]
Deserialization tests:
From: {"id":1,"phones":[{"id":3,"n":"a"},{"id":2,"n":null},3]}
To : {"id":1,"phones":[3,2,3]}
From: {"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":null},{"id":3,"n":"b"}]}
To : {"id":1,"phones":[3,2,3]}
From: {"id":1,"phones":[1,2,1]}
To : {"id":1,"phones":[1,2,1]}

ObjectMapper2 with ignoreJsonIdentityInfoMapper
Serialization tests:
Contact : {"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":"a"},{"id":3,"n":"b"}]}
List: [{"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":"a"},{"id":3,"n":"b"}]},{"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":"a"},{"id":3,"n":"b"}]}]
Deserialization tests:
From: {"id":1,"phones":[{"id":3,"n":"a"},{"id":2,"n":null},3]}
To : {"id":1,"phones":[{"id":3,"n":"a"},{"id":2,"n":null},{"id":3,"n":"a"}]}
From: {"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":null},{"id":3,"n":"b"}]}
To : {"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":null},{"id":3,"n":"b"}]}
From: {"id":1,"phones":[1,2,1]}
To : {"id":1,"phones":[{"id":1,"n":null},{"id":2,"n":null},{"id":1,"n":null}]}

ObjectMapper3 with ignoreJsonIdentityReferenceMapper
Serialization tests:
Contact : {"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":"a"},{"id":3,"n":"b"}]}
List: [{"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":"a"},{"id":3,"n":"b"}]},{"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":"a"},{"id":3,"n":"b"}]}]
Deserialization tests:
From: {"id":1,"phones":[{"id":3,"n":"a"},{"id":2,"n":null},3]}
To : {"id":1,"phones":[{"id":3,"n":"a"},{"id":2,"n":null},{"id":3,"n":"a"}]}
From: {"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":null},{"id":3,"n":"b"}]}
To : {"id":1,"phones":[{"id":3,"n":"b"},{"id":2,"n":null},{"id":3,"n":"b"}]}
From: {"id":1,"phones":[1,2,1]}
To : {"id":1,"phones":[{"id":1,"n":null},{"id":2,"n":null},{"id":1,"n":null}]}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment