Achaius
Achaius

Reputation: 6124

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

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.

Upvotes: 21

Views: 18068

Answers (3)

RandomCoder
RandomCoder

Reputation: 25

  public static <T> String getNonNullFieldsSerialized(T object, ObjectMapper objectMapper)throws JsonProcessingException {

    Map<String, Object> objectMap = objectMapper.convertValue(object, new TypeReference<Map<String, Object>>() {});

    Map<String, Object> objectMapNonNullValues = objectMap.entrySet().stream()
            .filter(stringObjectEntry -> Objects.nonNull(stringObjectEntry.getValue()))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    return objectMapper.writeValueAsString(objectMapNonNullValues);
  }

This will basically ignore all the fields that are non-null. Similarly you can ignore other fields by changing the map filter condition.

Upvotes: 2

abhinav kumar
abhinav kumar

Reputation: 1803

I have implemented dynamic filter on data getting from db and returning it using rest api.I have avoided using MappingJacksonValue.As it was getting issue while object chaining

@GetMapping("/courses")
    public ResponseEntity<JpaResponse> allCourse() throws Exception {
        JpaResponse response = null;
         ObjectMapper mapper = new ObjectMapper(); 
         mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        List<Course> course = service.findAllCourse();
        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("name","reviews");
        FilterProvider filterProvider = new SimpleFilterProvider().addFilter("jpafilter", filter).setFailOnUnknownId(false);
                ObjectWriter writer = mapper.writer(filterProvider);
        String writeValueAsString = writer.writeValueAsString(course);
        List<Course> resultcourse = mapper.readValue(writeValueAsString,List.class);
            response = new JpaResponse(HttpStatus.OK.name(),resultcourse);
            return new ResponseEntity<>(response, HttpStatus.OK);

}

public class JpaResponse {
        private String status;
        private Object data;
        public JpaResponse() {
            super();
        }
        public JpaResponse(String status, Object data) {
            super();
            this.status = status;
            this.data = data;
        }
}

Upvotes: 0

cassiomolin
cassiomolin

Reputation: 130837

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);

Upvotes: 40

Related Questions