Reputation: 1256
I'm trying to serialize a Java POJO and omit certain fields if some condition was not met.
The solution that I have so far looks like following:
public class PermissionSerializer extends StdSerializer<Object> implements ContextualSerializer {
private String role;
public PermissionSerializer() {
super(Object.class);
}
public PermissionSerializer(String role) {
super(Object.class);
this.role = role;
}
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
String curRole = SomeContext.getCurrentRole();
if (curRole.equals(role)) {
jgen.writeObject(value);
return;
}
jgen.writeNull();
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
String unit = null;
AllowedFor ann = null;
if (property != null) {
ann = property.getAnnotation(AllowedFor.class);
}
if (ann != null) {
unit = ann.role();
}
return new PermissionSerializer(unit);
}
}
Then in the POJO I'm using it like:
@JsonSerialize(using = PermissionSerializer.class)
@AllowedFor(role = "admin")
private String advancedField;
It actually works, except that it might be not the nicest way of achieving what I want.
However, there is another problem with this approach. If we don't have permissions to show the field, it is serialized as null
in the resulting JSON, even if I have @JsonInclude(JsonInclude.Include.NON_NULL)
on the class level.
Is it possible to actually ignore this field completely? Or maybe there is a better way conditionally ignore fields?
Upvotes: 4
Views: 4833
Reputation: 197
You need to implement a Custom Filter and add it to you ObjectMapper instance. Assuming you want to do more complex things than just check if a value is null or not then you could create a custom filter which uses something like an expression in an annotation on the property you want to conditionally serialize.
I built something similar recently - https://github.com/johnhunsley/conditional-jackson-property-filter
Upvotes: 1
Reputation: 3713
You could write a ResponseAdvice for decorating the response based on the authentication level.
First of all JsonIgnore
on the field you don't want to show, not even the key even if the value is null
.
@JsonIgnore
private String advancedField;
Now, set up the the ResponseAdvice
.
@ControllerAdvice
public class MyResponseBodyAdvisor implements ResponseBodyAdvice<String> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return returnType.getDeclaringClass().equals(MyClass.class) && converterType.equals(MappingJackson2MessageConverter.class);
}
@Override
public String beforeBodyWrite(String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, String>>(){});
String currentRole = SomeContext.getCurrentRole();
if(currentRole.equals(Roles.SomeRole)) {
map.put("advancedField", "advancedValue");
}
return mapper.writeValueAsString(map);
}
}
Pretty much that is all you have to do. The advantage of using this approach is that you are binded to the Spring
eco-system with this approach and can use all the goodies that it provides if you need Dependency Injection and all that. Also, its cleaner than using validation logic in the Serializer
. You should look into ResponseAdvice if you need better support to your use case.
Upvotes: 2