Skip to content

Instantly share code, notes, and snippets.

@abhi2495
Last active July 19, 2020 20:33
Show Gist options
  • Save abhi2495/2228247c6d65ed49118e79e491d2ed12 to your computer and use it in GitHub Desktop.
Save abhi2495/2228247c6d65ed49118e79e491d2ed12 to your computer and use it in GitHub Desktop.
/*
##################################################################################
##################################################################################
######### IF YOU FOUND THIS GIST USEFUL, PLEASE LEAVE A STAR. THANKS. ############
##################################################################################
##################################################################################
*/
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.Map;
public class CustomTypeUpdateContract {
@JsonSerialize(keyUsing = MyCustomTypeMapKeySerializer.class)
@JsonDeserialize(keyUsing = MyCustomTypeMapKeyDeserializer.class)
private Map<MyCustomType, MyCustomType> oldValToNewValMap;
public Map<MyCustomType, MyCustomType> getOldValToNewValMap() {
return oldValToNewValMap;
}
public void setOldValToNewValMap(Map<MyCustomType, MyCustomType> oldValToNewValMap) {
this.oldValToNewValMap = oldValToNewValMap;
}
}
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import java.lang.reflect.Field;
public class MyCustomTypeMapKeyDeserializer extends KeyDeserializer {
@Override
public MyCustomType deserializeKey(String serializedString, DeserializationContext ctxt) {
String[] fieldVals = serializedString.split(",");
Field[] fields = MyCustomType.class.getDeclaredFields();
MyCustomType myCustomType = new MyCustomType();
for (int index = 0; index < fieldVals.length; index++) {
String value = fieldVals[index];
if (value.equals("null")) {
continue;
}
try {
fields[index].setAccessible(true);
Class type = fields[index].getType();
if (type == Integer.class) {
fields[index].set(myCustomType, Integer.parseInt(value));
} else {
fields[index].set(myCustomType, value);
}
} catch (IllegalAccessException exception) {
}
}
return myCustomType;
}
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class MyCustomTypeMapKeySerializer extends JsonSerializer<MyCustomType> {
@Override
public void serialize(MyCustomType myCustomType, JsonGenerator gen, SerializerProvider serializers) throws IOException {
Field[] fields = MyCustomType.class.getDeclaredFields();
List<String> fieldVals = new ArrayList<>();
for (Field field : fields) {
field.setAccessible(true);
String fieldName = field.getName();
try {
if (!field.isSynthetic()) {
// This synthetic check is required because in order to collect execution data, JaCoCo instruments the
// classes under test which adds two members to the classes: A private static field $jacocoData and a private static method $jacocoInit().
// Both members are marked as synthetic.
fieldVals.add(String.valueOf(field.get(myCustomType)));
}
} catch (IllegalAccessException e) {
}
}
gen.writeFieldName(String.join(",", fieldVals));
}
}

Use Case:

  • The object to be serialized/deserialized has a Map field whose key is another object that Jackson can't serialize or deserialize implicitly
  • A third party library is responsible for serializing or deserializing the object eg. custom serialization of api response contracts in a spring webapplication. i.e the ObjectMapper cannot be accessed and configured directly

How am I doing it

  • Using @JsonSerialize before Map field to register a custom serializer for the key.
public class MyCustomType {
private String id;
private String name;
private int val;
public MyCustomType() {
}
public MyCustomType(String id, String name, int val) {
this.id = id;
this.name = name;
this.val = val;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
}
Map<MyCustomType, MyCustomType> oldValToNewValMap = new HashMap<>();
MyCustomType oldVal = new MyCustomType("ID#0001","type1",10);
MyCustomType newVal = new MyCustomType("ID#0001", "type1",11);
oldValToNewValMap.put(oldVal, newVal);
CustomTypeUpdateContract customTypeUpdateContract = new CustomTypeUpdateContract();
customTypeUpdateContract.setOldValToNewValMap(oldValToNewValMap);
System.out.println(new ObjectMapper().writeValueAsString(customTypeUpdateContract));
//outputs
//{"oldValToNewValMap":{"ID#0001,type1,10":{"id":"ID#0001","name":"type1","val":11}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment