Went down a path of creating an annotation that would dynamic determine whether a field should be serialized or not.
The annotation’s implementation is as follows:
@Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize(using = HiddenFieldSerializer.class) @Target(value = ElementType.FIELD) public @interface Hidden { }
Now the code for the Serializer:
public class HiddenFieldSerializer extends StdSerializer<String> implements ContextualSerializer { public HiddenFieldSerializer() { super(String.class); } @Override public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) { try { provider.defaultSerializeNull(jgen); } catch (IOException e) { } } @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) { return shouldHide() ? new HiddenFieldSerializer() : new StringSerializer(); } public boolean shouldHide() { /* Simplifying this */ return Boolean.TRUE; } }
A little bit of code to show how it works:
public class Test { static final ObjectMapper mapper = new ObjectMapper() .setSerializationInclusion(Include.NON_NULL) .setSerializationInclusion(Include.NON_EMPTY); static class User { @JsonProperty String username; @Hidden @JsonProperty String pin; } public static void main(String... args) throws JsonProcessingException { final POC.User u = new POC.User(); u.username = "harry_potter"; u.pin = "1298"; System.out.println(mapper.writeValueAsString(u)); } }
And the output is as follows:
{"username":"harry_potter","pin":null}
How do I get the field pin to be removed from the serialization instead of it being null? Obviously setting the mapper’s properties was of very little user in such a context. Any suggestions? Thoughts? Maybe the whole thing is a bad idea?
Ideally I should be able to see the following:
{"username":"harry_potter"}
Advertisement
Answer
It’s not clear whether you want to ignore a given property statically or dynamically. Anyways, looks like you have over-engineered it.
First of all, I want to make sure that you came across @JsonIgnore
before. If it doesn’t suit your needs, you could define your custom ignore annotation as following:
@Retention(RetentionPolicy.RUNTIME) public @interface Hidden { }
Then pick the approach that best suit your needs:
Approach #1
Extend JacksonAnnotationIntrospector
and override the method that checks for the ignore marker:
public class CustomAnnotationIntrospector extends JacksonAnnotationIntrospector { @Override public boolean hasIgnoreMarker(AnnotatedMember m) { return super.hasIgnoreMarker(m) || m.hasAnnotation(Hidden.class); } }
Configure ObjectMapper
to use your annotation introspector:
ObjectMapper mapper = new ObjectMapper(); mapper.setAnnotationIntrospector(new CustomAnnotationIntrospector());
The annotation introspection occurs only once per class so you can not dynamically change the criteria you use (if any). A similar example can be seen in this answer.
Approach #2
Extend BeanSerializerModifier
to modify the properties that will be serialized:
public class CustomBeanSerializerModifier extends BeanSerializerModifier { @Override public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) { return beanProperties.stream() .filter(property -> property.getAnnotation(Hidden.class) == null) .collect(Collectors.toList()); } }
Then add it to a Module
and register it to your ObjectMapper
:
ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new SimpleModule() { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.addBeanSerializerModifier(new CustomBeanSerializerModifier()); } });
This approach allows you to ignore properties dynamically.