Jackson Change JSONignore Dynamically

Jackson Change JsonIgnore Dynamically

For serialization, "filtering properties" blog entry should help. Deserialization side has less support, since it is more common to want to filter out stuff that is written.

One possible approach is to sub-class JacksonAnnotationIntrospector, override method(s) that introspect ignorability of methods (and/or fields) to use whatever logic you want.

It might also help if you gave an example of practical application, i.e what and why you are trying to prevent from being deserialized.

Spring and Jackson: set json ignore dynamically

I never use my Entitys for generating JSON, I think another set DTO classes will make you happier in the long run. My DTO typically has a constructor which takes the Entity as argument (it still needs a default constructor if you plan to use it for parsing incoming JSON).

If you really want to use your Entities, I would recommend that you use MixIns, which allows you to register a MixIn class, that augments the serialization of a specific class.

Here is a link to a MixIn example I made for another answer.

How to dynamically ignore a property on Jackson serialization

I've put together three approaches for performing dynamic filtering in Jackson. One of them must suit your needs.

Using @JsonView

You could use @JsonView:

public class Views {         
interface Simple { }
interface Detailed extends Simple { }
}
public class Foo {

@JsonView(Views.Simple.class)
private String name;

@JsonView(Views.Detailed.class)
private String details;

// Getters and setters
}
@RequestMapping("/foo")
@JsonView(Views.Detailed.class)
public Foo getFoo() {
Foo foo = new Foo();
return foo;
}

Alternatively you can set the view dynamically with MappingJacksonValue.

@RequestMapping("/foo")
public MappingJacksonValue getFoo() {
Foo foo = new Foo();
MappingJacksonValue result = new MappingJacksonValue(foo);
result.setSerializationView(Views.Detailed.class);
return result;
}

Using a BeanSerializerModifier

You could extend BeanSerializerModifier and then override the changeProperties() method. It allows you to add, remove or replace any of properties for serialization, according to your needs:

public class CustomSerializerModifier extends BeanSerializerModifier {

@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {

// In this method you can add, remove or replace any of passed properties

return beanProperties;
}
}

Then register the serializer as a module in your ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new SimpleModule() {

@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new CustomSerializerModifier());
}
});

Check examples here and here.

Using @JsonFilter with a SimpleBeanPropertyFilter

Another approach involves @JsonFilter:

@JsonFilter("customPropertyFilter")
public class Foo {

private String name;
private String details;

// Getters and setters
}

Extend SimpleBeanPropertyFilter and override the serializeAsField() method according to your needs:

public class CustomPropertyFilter extends SimpleBeanPropertyFilter {

@Override
public void serializeAsField(Object pojo, JsonGenerator jgen,
SerializerProvider provider,
PropertyWriter writer) throws Exception {

// Serialize a field
// writer.serializeAsField(pojo, jgen, provider, writer);

// Omit a field from serialization
// writer.serializeAsOmittedField(pojo, jgen, provider);
}
}

Then register the filter in your ObjectMapper:

FilterProvider filterProvider = new SimpleFilterProvider()
.addFilter("customPropertyFilter", new CustomPropertyFilter());

ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);

If you want to make your filter "global", that is, to be applied to all beans, you can create a mix-in class and annotate it with @JsonFilter("customPropertyFilter"):

@JsonFilter("customPropertyFilter")
public class CustomPropertyFilterMixIn {

}

Then bind the mix-in class to Object:

mapper.addMixIn(Object.class, CustomPropertyFilterMixIn.class);

Dynamic change of JsonProperty name using Jackson java library

You can provide name in constructor and use JsonAnyGetter. Below solution:

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class JsonApp {

public static void main(String[] args) throws Exception {
ResponseEntity entity = new ResponseEntity("dynList",
Collections.singletonList(Collections.singletonMap("key", "value1")));

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);

System.out.println(mapper.writeValueAsString(entity));
}
}

class ResponseEntity implements Serializable {

private static final long serialVersionUID = 1L;
private int total_record_count;
private int filtered_record_count;

private String propertyName;

@JsonIgnore
private List<Map<String, Object>> entityList;

public ResponseEntity(String propertyName, List<Map<String, Object>> entityList) {
this.propertyName = propertyName;
this.entityList = entityList;
this.filtered_record_count = entityList.size();
}

@JsonAnyGetter
public Map<String, Object> otherProperties() {
return Collections.singletonMap(propertyName, entityList);
}

// other methods
}

prints:

{
"total_record_count" : 0,
"filtered_record_count" : 1,
"dynList" : [ {
"key" : "value1"
} ]
}

Custom jackson bean serializer that can dynamically ignore some annotated properties based on the value of a sibling property

Although I did try overriding the BeanPropertyWriter earlier but couldn't really figure it all out together how to do what I wanted to do.

Here is what I did finally.

Interface to implement if the bean class needs to hide it's fields dynamically..

public interface WithFieldsToHide {
boolean hide(String fieldId);
}

Annotation to put on the fields that need to be hidden..

@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface HideFieldId {
String id();
}
public class CustomFieldHideModule extends Module {
@Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new CustomFieldHideSerializerModifier());
}

private static class CustomFieldHideSerializerModifier extends BeanSerializerModifier {

@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
// only try to hide fields for class of a particular interface type
if (WithFieldsToHide.class.isAssignableFrom(beanDesc.getBeanClass())) {
for (int i = 0; i < beanProperties.size(); i++) {
BeanPropertyWriter writer = beanProperties.get(i);
// to be able to run the logic to hide, we need to know if the property has a field id assigned
HideFieldId annotation = writer.getAnnotation(HideFieldId.class);
if (null != annotation) {
beanProperties.set(i, new ConditionalPropertyWriter<>(writer, annotation.id()));
}
}
}
return beanProperties;
}

private static class ConditionalPropertyWriter<T extends WithFieldsToHide> extends BeanPropertyWriter{

private final fieldId;
protected ConditionalPropertyWriter(BeanPropertyWriter writer, String fieldId) {
super(writer);
this.fieldId = fieldId;
}

@Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception {
T withFieldsToHide = (T) bean; // don't need type check as we're checking when registering this writer for the property
if (!withFieldsToHide.isInternal(fieldId)) {
super.serializeAsField(bean, jgen, prov);
}
}
}
}
}

Then you just register this module with your object mapper, and voila!

I also wanted one more level of hiding, for map property types where i only needed to hide certain elements in the map property and not the whole property itself, and could write something to achieve it. Let me know if anyone wants that, not including it for brevity.

Thanks!!



Related Topics



Leave a reply



Submit