Last active
October 7, 2017 17:42
-
-
Save varren/ba5efcaac434e8ec5a291e26a5c380ae to your computer and use it in GitHub Desktop.
Jackson @JsonIdentityInfo with always embedded whole objects during serialization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
OUTPUT: