Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically ignore a property on Jackson serialization

I have an entity with multiple @ManyToOne associations. I am using spring-boot to expose a REST API. Currently, I have multiple REST API's which return a JSON response of the whole entity, including associations.

But I don't want to serialize all associated objects in all REST APIs.

For example

  • API-1 should return parent + associationA object
  • API-2 should return parent + associationA + associationB object
  • API-3 should return parent + associationB + associationC + associationD

So, in my serialization process, I want to ignore all association except associationA for API-1.

For API-2 I want to ignore other associations except A and B


How do I dynamically ignore these properties during Jackson serialization?

Notes: I'm using the same class for each; I am not interested in creating a DTO for each API.

Any suggestions are kingly appreciated.

like image 313
Achaius Avatar asked Jul 04 '18 11:07

Achaius


People also ask

How do you ignore property serializable?

WhenWritingDefault - The property is ignored on serialization if it's a reference type null , a nullable value type null , or a value type default . WhenWritingNull - The property is ignored on serialization if it's a reference type null , or a nullable value type null .

How do you tell Jackson to ignore a field during deserialization?

Overview So when Jackson is reading from JSON string, it will read the property and put into the target object. But when Jackson attempts to serialize the object, it will ignore the property. For this purpose, we'll use @JsonIgnore and @JsonIgnoreProperties.

How do I ignore a field in JSON response Jackson?

The Jackson @JsonIgnore annotation can be used to ignore a certain property or field of a Java object. The property can be ignored both when reading JSON into Java objects and when writing Java objects into JSON.


1 Answers

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);
like image 67
cassiomolin Avatar answered Sep 28 '22 00:09

cassiomolin