jackson-databind
jackson-databind copied to clipboard
Inconsistency between @JsonInclude and ObjectMapper.setDefaultPropertyInclusion() for default values
Describe the bug There is an inconsistency in how @JsonInclude and ObjectMapper.setDefaultPropertyInclusion(). If you use @JsonInclude you can use custom default values and for those defaults the value will be removed at serialisation. If you use ObjectMapper.setDefaultPropertyInclusion() it will consider as default values 0 for numbers and empty string instead those you define.
Version information 2.13.3
To Reproduce
public class JsonIncludeNonDefaults {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
System.out.println(objectMapper.writer().writeValueAsString(new A(0,"")));
System.out.println(objectMapper.writer().writeValueAsString(new A(1,"value")));
System.out.println(objectMapper.writer().writeValueAsString(new A(2,"default")));
}
@Data
@NoArgsConstructor
@AllArgsConstructor
// @JsonInclude(JsonInclude.Include.NON_DEFAULT) // uncomment for version with annotation
private static class A{
private int i=2;
private String s="default";
}
}
Result with @JsonInclude:
{"i":0,"s":""}
{"i":1,"s":"value"}
{}
Here the default values are 2 and "default" and those are not included in the serialisation.
Result without @JsonInclude that uses the default value set by objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);'
:
{}
{"i":1,"s":"value"}
{"i":2,"s":"default"}
Here it completely ignores your default values and it uses 0 and empty String which is not as expected (you want to use your defined default values).
I believe the issue is in Property builder where _useRealPropertyDefaults is true only for annotation version.
public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc) {
this._config = config;
this._beanDesc = beanDesc;
Value inclPerType = Value.merge(beanDesc.findPropertyInclusion(Value.empty()), config.getDefaultPropertyInclusion(beanDesc.getBeanClass(), Value.empty()));
this._defaultInclusion = Value.merge(config.getDefaultPropertyInclusion(), inclPerType);
this._useRealPropertyDefaults = inclPerType.getValueInclusion() == Include.NON_DEFAULT;
this._annotationIntrospector = this._config.getAnnotationIntrospector();
}
Expected behavior Both @JsonInclude and ObjectMapper.setDefaultPropertyInclusion() should be equivalent and both should take into account your default value. The purpose for default is to define a programatic way to configure when you don't have access to the class (e.g. the class is generated).