Assuem that i have this below classes:
public class Employee {
private Department department;
// other fields, getters and setters omited for brevtity
}
public class Department {
private Address address;
// other fields, getters and setters omited for brevtity
}
public class Address {
private Location location;
// other fields, getters and setters omited for brevtity
}
public class Location {
private String streetName;
// other fields, getters and setters omited for brevtity
}
now, i want to load Employee
object and serialize it with ObjectMapper
:
public void serializeEmployee() {
Employee employee = entityManager.load(Employee.class, 1);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(student));
}
when i run above code, i see json string like this:
{
"department" : {
"address" : {
"location" : {
"streetName" : {}
}
}
}
}
but i want to set serialization depth to one level, i mean when the code run, i want to see result like this:
{
"department" : {
}
}
NOTE
I don't want to use jackson annotations, i want to set configuration when using mapper
object. for exmaple with calling mapper.setConfig
or mapper.disable
.
You can use PropertyFilter to be able to limit the depth of serialization. Here's a solution I came up with.couple of things to note when using this filter, which are also demonstrated in the example:
ObjectMapper
for each depth.Here's the code. The DeepFieldFilter does depth-calculation. It has to be registered as a filter in ObjectMapper
and the data class has to marked with @JsonFilter
annotation.
public class JsonSerializationDeepFun {
@Data
@JsonFilter("depth_3")
static class DynamicJsonObject {
Long id;
String name;
BigDecimal price;
List<DynamicJsonObject> children = new ArrayList<>();
@JsonIgnore
Map<String, Object> properties = new HashMap<>();
@JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
@JsonAnyGetter
public Map<String, Object> getMap() {
return properties;
}
}
/**
* There're a couple of things to note when using this filter. <a href="https://stackoverflow.com/a/51279460/1961634">Visit stackoverflow for an example</a>
* <ul>
* <li>arrays provide an additional depth level, so array entry is depth+2
* from parent; it's not a bug, it's a feature - this behavior could be
* changed if JsonStreamContext is parsed for array start</li>
* <li>map properties are serialized fully at the level the map is declared</li>
* <li>depth is defined per-class; you could extend base class with DN suffix
* to serialize if you need variable length, or make a constant filter name
* and create a dedicated `ObjectMapper` for each depth</li>
* </ul>
* @author Dariusz Wawer <[email protected]>
*
*/
static class DeepFieldFilter extends SimpleBeanPropertyFilter {
private final int maxDepth;
public DeepFieldFilter(int maxDepth) {
super();
this.maxDepth = maxDepth;
}
private int calcDepth(PropertyWriter writer, JsonGenerator jgen) {
JsonStreamContext sc = jgen.getOutputContext();
int depth = -1;
while (sc != null) {
sc = sc.getParent();
depth++;
}
return depth;
}
@Override
public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
int depth = calcDepth(writer, gen);
if (depth <= maxDepth) {
writer.serializeAsField(pojo, gen, provider);
}
// comment this if you don't want {} placeholders
else {
writer.serializeAsOmittedField(pojo, gen, provider);
}
}
}
public static void main(String[] args) throws IOException {
ObjectMapper om = new ObjectMapper();
SimpleFilterProvider depthFilters = new SimpleFilterProvider().addFilter("depth_1", new DeepFieldFilter(1))
.addFilter("depth_2", new DeepFieldFilter(2))
.addFilter("depth_3", new DeepFieldFilter(3))
.addFilter("depth_4", new DeepFieldFilter(4))
.addFilter("depth_5", new DeepFieldFilter(5))
// ...
;
om.setFilterProvider(depthFilters);
om.enable(SerializationFeature.INDENT_OUTPUT);
DynamicJsonObject obj = new DynamicJsonObject();
obj.setId(321L);
obj.setName("name");
obj.setPrice(BigDecimal.valueOf(10.0));
Map<String, Object> mapD3 = new HashMap<>();
mapD3.put("depth", "3");
mapD3.put("info", "gets serialzied at depth 1");
Map<String, Object> mapD2 = new HashMap<>();
mapD2.put("depth", "2");
mapD2.put("map", mapD3);
Map<String, Object> mapD1 = new HashMap<>();
mapD1.put("depth", "1");
mapD1.put("map", mapD2);
obj.setProperties(mapD1);
DynamicJsonObject child = new DynamicJsonObject();
child.setId(514L);
child.setName("actually depth 3, because array");
child.setPrice(BigDecimal.valueOf(5.1));
obj.getChildren().add(child);
String jsonStr = om.writeValueAsString(obj);
System.out.println(jsonStr);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With