Skip to content

Commit 75098b7

Browse files
committed
Fix #1351
1 parent b72b56c commit 75098b7

File tree

6 files changed

+67
-17
lines changed

6 files changed

+67
-17
lines changed

release-notes/CREDITS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,10 @@ Andrew Snare (asnare@github)
530530
* Reported #1315: Binding numeric values can BigDecimal lose precision
531531
(2.8.2)
532532

533+
Gili Tzabari (cowwoc@github)
534+
* Reported #1351: `@JsonInclude(NON_DEFAULT)` doesn't omit null fields
535+
(2.8.3)
536+
533537
Connor Kuhn (ckuhn@github)
534538
* Contributed #1341: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY
535539
(2.9.0)

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Project: jackson-databind
1111

1212
2.8.3 (not yet released)
1313

14+
#1351: `@JsonInclude(NON_DEFAULT)` doesn't omit null fields
15+
(reported by Gili T)
1416
#1353: Improve error-handling for `java.net.URL` deserialization
1517
#1361: Change `TokenBuffer` to use new `writeEmbeddedObject()` if possible
1618

src/main/java/com/fasterxml/jackson/databind/BeanDescription.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ protected BeanDescription(JavaType type) {
195195
*/
196196

197197
/**
198+
* Method for finding annotation-indicated inclusion definition (if any);
199+
* possibly overriding given default value.
200+
*<p>
201+
* NOTE: does NOT use global inclusion default settings as the base, unless
202+
* passed as `defValue`.
203+
*
198204
* @since 2.7
199205
*/
200206
public abstract JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue);

src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,7 @@ public JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue) {
391391
if (_annotationIntrospector != null) {
392392
JsonInclude.Value incl = _annotationIntrospector.findPropertyInclusion(_classInfo);
393393
if (incl != null) {
394-
return (defValue == null) ? incl
395-
: defValue.withOverrides(incl);
394+
return (defValue == null) ? incl : defValue.withOverrides(incl);
396395
}
397396
}
398397
return defValue;

src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,10 @@ public class PropertyBuilder
1616
{
1717
// @since 2.7
1818
private final static Object NO_DEFAULT_MARKER = Boolean.FALSE;
19-
19+
2020
final protected SerializationConfig _config;
2121
final protected BeanDescription _beanDesc;
2222

23-
/**
24-
* Default inclusion mode for properties of the POJO for which
25-
* properties are collected; possibly overridden on
26-
* per-property basis.
27-
*/
28-
final protected JsonInclude.Value _defaultInclusion;
29-
3023
final protected AnnotationIntrospector _annotationIntrospector;
3124

3225
/**
@@ -40,14 +33,41 @@ public class PropertyBuilder
4033
*/
4134
protected Object _defaultBean;
4235

36+
/**
37+
* Default inclusion mode for properties of the POJO for which
38+
* properties are collected; possibly overridden on
39+
* per-property basis. Combines global inclusion defaults and
40+
* per-type (annotation and type-override) inclusion overrides.
41+
*/
42+
final protected JsonInclude.Value _defaultInclusion;
43+
44+
/**
45+
* Marker flag used to indicate that "real" default values are to be used
46+
* for properties, as per per-type value inclusion of type <code>NON_DEFAULT</code>
47+
*
48+
* @since 2.8
49+
*/
50+
final protected boolean _useRealPropertyDefaults;
51+
4352
public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc)
4453
{
4554
_config = config;
4655
_beanDesc = beanDesc;
47-
// NOTE: this includes global defaults and defaults of POJO that contains property,
48-
// but not defaults for types of properties referenced
49-
_defaultInclusion = beanDesc.findPropertyInclusion(
50-
config.getDefaultPropertyInclusion(beanDesc.getBeanClass()));
56+
// 08-Sep-2016, tatu: This gets tricky, with 3 levels of definitions:
57+
// (a) global default inclusion
58+
// (b) per-type default inclusion (from annotation or config overrides;
59+
// latter having precedence
60+
// Cc) per-property override
61+
//
62+
// and not only requiring merging, but also considering special handling
63+
// for NON_DEFAULT in case of (b) (vs (a) or (c))
64+
JsonInclude.Value inclPerType = JsonInclude.Value.merge(
65+
beanDesc.findPropertyInclusion(JsonInclude.Value.empty()),
66+
config.getDefaultPropertyInclusion(beanDesc.getBeanClass(),
67+
JsonInclude.Value.empty()));
68+
_defaultInclusion = JsonInclude.Value.merge(config.getDefaultPropertyInclusion(),
69+
inclPerType);
70+
_useRealPropertyDefaults = inclPerType.getValueInclusion() == JsonInclude.Include.NON_DEFAULT;
5171
_annotationIntrospector = _config.getAnnotationIntrospector();
5272
}
5373

@@ -123,15 +143,16 @@ protected BeanPropertyWriter buildWriter(SerializerProvider prov,
123143
// so that if enclosing class has this, we may need to access values of property,
124144
// whereas for global defaults OR per-property overrides, we have more
125145
// static definition. Sigh.
126-
// First: case of class specifying it; try to find POJO property defaults
127-
if (_defaultInclusion.getValueInclusion() == JsonInclude.Include.NON_DEFAULT) {
146+
// First: case of class/type specifying it; try to find POJO property defaults
147+
if (_useRealPropertyDefaults) {
128148
// 07-Sep-2016, tatu: may also need to front-load access forcing now
129149
if (prov.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)) {
130150
am.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
131151
}
132152
valueToSuppress = getPropertyDefaultValue(propDef.getName(), am, actualType);
133153
} else {
134154
valueToSuppress = getDefaultValue(actualType);
155+
suppressNulls = true;
135156
}
136157
if (valueToSuppress == null) {
137158
suppressNulls = true;
@@ -140,7 +161,6 @@ protected BeanPropertyWriter buildWriter(SerializerProvider prov,
140161
valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
141162
}
142163
}
143-
144164
break;
145165
case NON_ABSENT: // new with 2.6, to support Guava/JDK8 Optionals
146166
// always suppress nulls

src/test/java/com/fasterxml/jackson/databind/filter/JsonIncludeTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,23 @@ public void testPropConfigOverridesForInclude() throws IOException
261261
assertEquals(aposToQuotes("{'map':{}}"),
262262
mapper.writeValueAsString(empty));
263263
}
264+
265+
static class Issue1351Bean
266+
{
267+
public final String first;
268+
public final double second;
269+
270+
public Issue1351Bean(String first, double second) {
271+
this.first = first;
272+
this.second = second;
273+
}
274+
}
275+
276+
public void testIssue1351() throws Exception
277+
{
278+
ObjectMapper mapper = new ObjectMapper();
279+
mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
280+
assertEquals(aposToQuotes("{}"),
281+
mapper.writeValueAsString(new Issue1351Bean(null, (double) 0)));
282+
}
264283
}

0 commit comments

Comments
 (0)