Technical
Depth wise JSON Serializer

Administrator - 31 August 2014 - 12min
Administrator - 31 August 2014 - 12min
Very often we land in situation with cyclic reference if target entity has self references. There are multiple solution available over internet, but none of these will solve your problem. The only option is left to write own depth wise serializer.
@SuppressWarnings("serial") @Entity @Table(name = "user") public class User extends AbstractAuditableEntity { private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } @SuppressWarnings("serial") @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public class AbstractAuditableEntity extends AbstractPersistable<Long> implements Auditable<User, Long> { @Version protected Long version; @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime") @DateTimeFormat(iso = ISO.DATE_TIME) protected DateTime createdDate; @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime") @DateTimeFormat(iso = ISO.DATE_TIME) protected DateTime lastModifiedDate; protected User createdBy; protected User lastModifiedBy; @Enumerated(EnumType.STRING) protected StatusType status; public Long getVersion() { return version; } public void setVersion(Long version) { this.version = version; } public DateTime getCreatedDate() { return createdDate; } public void setCreatedDate(DateTime createdDate) { this.createdDate = createdDate; } public DateTime getLastModifiedDate() { return lastModifiedDate; } public void setLastModifiedDate(DateTime lastModifiedDate) { this.lastModifiedDate = lastModifiedDate; } public User getCreatedBy() { return createdBy; } public void setCreatedBy(User createdBy) { this.createdBy = createdBy; } public User getLastModifiedBy() { return lastModifiedBy; } public void setLastModifiedBy(User lastModifiedBy) { this.lastModifiedBy = lastModifiedBy; } public StatusType getStatus() { return status; } public void setStatus(StatusType status) { this.status = status; } }
@Test public void serialize() throws JsonProcessingException { User user = new User(); user.setFirstName("First Name"); user.setLastName("Last Name"); user.setCreatedBy(user); user.setLastModifiedBy(user); ObjectMapper objectMapper = new ObjectMapper(); String userString = objectMapper.writeValueAsString(user); System.out.println("JSON String :: " + userString); }
com.fasterxml.jackson.databind.JsonMappingException: Direct self-reference leading to cycle (through reference chain: com.abid.learning.serialization.entity.User["createdBy"])
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._handleSelfReference(BeanPropertyWriter.java:667)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:540)
private static ThreadLocal<Integer> depth = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0; } };
depth.set(depth.get() + 1); if (depth.get() > 2) { jgen.writeNull(); } else { ... }
public class UserJsonSerializer extends JsonSerializer<User> { private static ThreadLocal<Integer> depth = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0; } }; @Override public void serialize(User value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { depth.set(depth.get() + 1); try { if (depth.get() > 2) { jgen.writeNull(); } else { jgen.writeStartObject(); jgen.writeStringField("firstName", value.getFirstName()); jgen.writeStringField("lastName", value.getLastName()); User createdByUser = value.getCreatedBy(); if (null != createdByUser) { jgen.writeStringField( "createdBy", createdByUser.getFirstName() + " " + createdByUser.getLastName()); } User lastModifiedByUser = value.getLastModifiedBy(); if (null != lastModifiedByUser) { jgen.writeStringField("lastModifiedBy", lastModifiedByUser.getFirstName() + " " + lastModifiedByUser.getLastName()); } jgen.writeEndObject(); return; } } catch (Exception ex) { ex.printStackTrace(); } } }
ObjectMapper objectMapper = new ObjectMapper(); SimpleModule userModule = new SimpleModule(); userModule.addSerializer(User.class, new UserJsonSerializer()); objectMapper.registerModule(userModule);
public class UserTest { @Test public void serialize() throws JsonProcessingException { User user = new User(); user.setFirstName("First Name"); user.setLastName("Last Name"); user.setCreatedBy(user); user.setLastModifiedBy(user); ObjectMapper objectMapper = new ObjectMapper(); SimpleModule userModule = new SimpleModule(); userModule.addSerializer(User.class, new UserJsonSerializer()); objectMapper.registerModule(userModule); String userString = objectMapper.writeValueAsString(user); System.out.println("JSON String :: " + userString); } }
JSON String :: {"firstName":"First Name","lastName":"Last Name","createdBy":"First Name Last Name","lastModifiedBy":"First Name Last Name"}
Leave a Reply