Search code examples
javajsonspringjacksonobjectmapper

How can I exclude fields with custom annotation in Spring Boot ObjectMapper


I have a need to have two different ObjectMapper in the application.

Pojo I am working with:

public class Student {

 private String name;
 private Integer age;

 @HideThisField
 private String grade; 

 // getters & setters..
}

One is the out of the box configuration based ObjectMapper as below:

@Bean("objectMapper")
public ObjectMapper getRegularObjectMapper() {
     //With some configurations 
     return new ObjectMapper();
}

I need another ObjectMapper that while serializing ignores a few fields for all objects based on an annotation on a field.

@Bean("customObjectMapper")
public ObjectMapper getCustomObjectMapper() {
   // This is where i want to ignore the fields with @HideThisField
   return new ObjectMapper();
}

Output of the two mappers:

objectMapper.writeValuesAsString(someStudent) prints:

{"name": ""student1", age: 10, "grade": "A+"}

customObjectMapper.writeValuesAsString(someStudent) prints:

{"name": ""student1", age: 10}


Solution

  • JacksonAnnotationIntrospector handles standard Jackson annotations. Overriding the hasIgnoreMarker method, you can make it work according to your own annotation.

    import com.fasterxml.jackson.core.*;
    import com.fasterxml.jackson.databind.*;
    import com.fasterxml.jackson.databind.introspect.*;
    
    import java.lang.annotation.*;
    
    public class StudentExample {
    
        public static void main(String[] args) throws JsonProcessingException {
            Student student = new Student();
            student.setName("Student 1");
            student.setAge(10);
            student.setGrade("A+");
    
            String st1 = getRegularObjectMapper().writeValueAsString(student);
            String st2 = getCustomObjectMapper().writeValueAsString(student);
    
            System.out.println(st1);
            System.out.println(st2);
        }
    
        public static ObjectMapper getRegularObjectMapper() {
            return new ObjectMapper();
        }
    
        public static ObjectMapper getCustomObjectMapper() {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
                @Override
                public boolean hasIgnoreMarker(AnnotatedMember m) {
                    if (_findAnnotation(m, HideThisField.class) != null)
                        return true;
                    return false;
                }
            });
            return objectMapper;
        }
    }
    
    class Student {
    
        private String name;
        private Integer age;
    
        @HideThisField
        private String grade;
    
        public Student() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getGrade() {
            return grade;
        }
    
        public void setGrade(String grade) {
            this.grade = grade;
        }
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface HideThisField {}
    

    The console output is:

    {"name":"Student 1","age":10,"grade":"A+"}
    {"name":"Student 1","age":10}
    

    getCustomObjectMapper() don't skips JsonIgnore annotations because you override the standard, if you want, you need to add this to the if block.