I have a tree object structure that I need to Serialize and I want to be able to control the depth of the serialization every time based on a parameter using jackson (or any oth
You need to implement custom serializer in which you need to count number of already handled Node
objects. For each serialization process we need to provide max depth
value and decrement it every time Node
class is spotted. Example implementation:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializer;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
public class JsonApp {
public static void main(String[] args) throws IOException {
Node nodeD = new Node("D", null);
Node nodeC = new Node("C", nodeD);
Node nodeB = new Node("B", nodeC);
Node nodeA = new Node("A", nodeB);
for (int i = 0; i < 4; i++) {
System.out.println("Depth: " + i);
System.out.println(serialiseWithDepth(nodeA, i));
}
}
private static ObjectMapper mapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.addModule(createNodeModule())
.build();
private static String serialiseWithDepth(Node node, int maxDepth) throws JsonProcessingException {
ObjectWriter writer = mapper.writerFor(Node.class)
.withAttribute(NodeDepthBeanSerializer.DEPTH_KEY, new AtomicInteger(maxDepth));
return writer.writeValueAsString(node);
}
private static SimpleModule createNodeModule() {
SimpleModule nodeModule = new SimpleModule("NodeModule");
nodeModule.setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass() == Node.class) {
return new NodeDepthBeanSerializer((BeanSerializerBase) serializer);
}
return super.modifySerializer(config, beanDesc, serializer);
}
});
return nodeModule;
}
}
class NodeDepthBeanSerializer extends BeanSerializer {
public static final String DEPTH_KEY = "maxDepthSize";
public NodeDepthBeanSerializer(BeanSerializerBase src) {
super(src);
}
@Override
protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException {
AtomicInteger depth = (AtomicInteger) provider.getAttribute(DEPTH_KEY);
if (depth.decrementAndGet() >= 0) {
super.serializeFields(bean, gen, provider);
}
}
}
Above code prints:
Depth: 0
Node{id='A', child=Node{id='B', child=Node{id='C', child=Node{id='D', child=null}}}} => 0
{ }
Depth: 1
Node{id='A', child=Node{id='B', child=Node{id='C', child=Node{id='D', child=null}}}} => 1
Node{id='B', child=Node{id='C', child=Node{id='D', child=null}}} => 0
{
"id" : "A",
"child" : { }
}
Depth: 2
Node{id='A', child=Node{id='B', child=Node{id='C', child=Node{id='D', child=null}}}} => 2
Node{id='B', child=Node{id='C', child=Node{id='D', child=null}}} => 1
Node{id='C', child=Node{id='D', child=null}} => 0
{
"id" : "A",
"child" : {
"id" : "B",
"child" : { }
}
}
Depth: 3
Node{id='A', child=Node{id='B', child=Node{id='C', child=Node{id='D', child=null}}}} => 3
Node{id='B', child=Node{id='C', child=Node{id='D', child=null}}} => 2
Node{id='C', child=Node{id='D', child=null}} => 1
Node{id='D', child=null} => 0
{
"id" : "A",
"child" : {
"id" : "B",
"child" : {
"id" : "C",
"child" : { }
}
}
}