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"}
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:
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.
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.