Skip to content

Commit 2c032ef

Browse files
committed
Fix #2592: JsonInclude support for any getters
Fix will not work if filter is used as it triggers separate execution path.
1 parent 8b6cabf commit 2c032ef

File tree

3 files changed

+107
-23
lines changed

3 files changed

+107
-23
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void getAndSerialize(Object bean, JsonGenerator gen, SerializerProvider p
5959
}
6060
// 23-Feb-2015, tatu: Nasty, but has to do (for now)
6161
if (_mapSerializer != null) {
62-
_mapSerializer.serializeFields((Map<?,?>) value, gen, provider);
62+
_mapSerializer.serializeWithoutTypeInfo((Map<?,?>) value, gen, provider);
6363
return;
6464
}
6565
_serializer.serialize(value, gen, provider);

src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java

+16-22
Original file line numberDiff line numberDiff line change
@@ -628,21 +628,7 @@ public void serialize(Map<?,?> value, JsonGenerator gen, SerializerProvider prov
628628
throws IOException
629629
{
630630
gen.writeStartObject(value);
631-
if (!value.isEmpty()) {
632-
if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
633-
value = _orderEntries(value, gen, provider);
634-
}
635-
PropertyFilter pf;
636-
if ((_filterId != null) && (pf = findPropertyFilter(provider, _filterId, value)) != null) {
637-
serializeFilteredFields(value, gen, provider, pf, _suppressableValue);
638-
} else if ((_suppressableValue != null) || _suppressNulls) {
639-
serializeOptionalFields(value, gen, provider, _suppressableValue);
640-
} else if (_valueSerializer != null) {
641-
serializeFieldsUsing(value, gen, provider, _valueSerializer);
642-
} else {
643-
serializeFields(value, gen, provider);
644-
}
645-
}
631+
serializeWithoutTypeInfo(value, gen, provider);
646632
gen.writeEndObject();
647633
}
648634

@@ -655,6 +641,21 @@ public void serializeWithType(Map<?,?> value, JsonGenerator gen, SerializerProvi
655641
gen.setCurrentValue(value);
656642
WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen,
657643
typeSer.typeId(value, JsonToken.START_OBJECT));
644+
serializeWithoutTypeInfo(value, gen, provider);
645+
typeSer.writeTypeSuffix(gen, typeIdDef);
646+
}
647+
648+
/*
649+
/**********************************************************
650+
/* Secondary serialization methods
651+
/**********************************************************
652+
*/
653+
654+
/**
655+
* General-purpose serialization for contents without writing object type. Will suppress, filter and
656+
* use custom serializers.
657+
*/
658+
public void serializeWithoutTypeInfo(Map<?, ?> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
658659
if (!value.isEmpty()) {
659660
if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
660661
value = _orderEntries(value, gen, provider);
@@ -670,15 +671,8 @@ public void serializeWithType(Map<?,?> value, JsonGenerator gen, SerializerProvi
670671
serializeFields(value, gen, provider);
671672
}
672673
}
673-
typeSer.writeTypeSuffix(gen, typeIdDef);
674674
}
675675

676-
/*
677-
/**********************************************************
678-
/* Secondary serialization methods
679-
/**********************************************************
680-
*/
681-
682676
/**
683677
* General-purpose serialization for contents, where we do not necessarily know
684678
* the value serialization, but

src/test/java/com/fasterxml/jackson/databind/ser/AnyGetterTest.java

+90
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.fasterxml.jackson.annotation.*;
77
import com.fasterxml.jackson.core.JsonGenerator;
88
import com.fasterxml.jackson.databind.*;
9+
import com.fasterxml.jackson.databind.ser.impl.*;
910
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
1011
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
1112
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
@@ -131,6 +132,37 @@ public void serialize(String value, JsonGenerator gen,
131132
}
132133
}
133134

135+
static class Bean2592NoAnnotations
136+
{
137+
protected Map<String, String> properties = new HashMap<>();
138+
139+
@JsonAnyGetter
140+
public Map<String, String> getProperties() {
141+
return properties;
142+
}
143+
144+
public void setProperties(Map<String, String> properties) {
145+
this.properties = properties;
146+
}
147+
148+
public void add(String key, String value) {
149+
properties.put(key, value);
150+
}
151+
}
152+
153+
static class Bean2592PropertyIncludeNonEmpty extends Bean2592NoAnnotations
154+
{
155+
@JsonInclude(content = JsonInclude.Include.NON_EMPTY)
156+
@JsonAnyGetter
157+
@Override
158+
public Map<String, String> getProperties() {
159+
return properties;
160+
}
161+
}
162+
163+
@JsonFilter("Bean2592")
164+
static class Bean2592WithFilter extends Bean2592NoAnnotations {}
165+
134166
/*
135167
/**********************************************************
136168
/* Test methods
@@ -196,4 +228,62 @@ public void testAnyGetterWithValueSerializer() throws Exception
196228
String json = mapper.writeValueAsString(input);
197229
assertEquals("{\"key\":\"VALUE\"}", json);
198230
}
231+
232+
// [databind#2592]
233+
public void testAnyGetterWithMapperDefaultIncludeNonEmpty() throws Exception
234+
{
235+
ObjectMapper mapper = new ObjectMapper()
236+
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
237+
Bean2592NoAnnotations input = new Bean2592NoAnnotations();
238+
input.add("non-empty", "property");
239+
input.add("empty", "");
240+
input.add("null", null);
241+
String json = mapper.writeValueAsString(input);
242+
assertEquals("{\"non-empty\":\"property\"}", json);
243+
}
244+
245+
// [databind#2592]
246+
public void testAnyGetterWithMapperDefaultIncludeNonEmptyAndFilterOnBean() throws Exception
247+
{
248+
FilterProvider filters = new SimpleFilterProvider()
249+
.addFilter("Bean2592", SimpleBeanPropertyFilter.serializeAllExcept("something"));
250+
ObjectMapper mapper = new ObjectMapper()
251+
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
252+
.setFilterProvider(filters);
253+
Bean2592WithFilter input = new Bean2592WithFilter();
254+
input.add("non-empty", "property");
255+
input.add("empty", "");
256+
input.add("null", null);
257+
String json = mapper.writeValueAsString(input);
258+
// Unfortunately path for bean with filter is different. It still skips nulls.
259+
assertEquals("{\"non-empty\":\"property\",\"empty\":\"\"}", json);
260+
}
261+
262+
// [databind#2592]
263+
public void testAnyGetterWithPropertyIncludeNonEmpty() throws Exception
264+
{
265+
ObjectMapper mapper = new ObjectMapper();
266+
Bean2592PropertyIncludeNonEmpty input = new Bean2592PropertyIncludeNonEmpty();
267+
input.add("non-empty", "property");
268+
input.add("empty", "");
269+
input.add("null", null);
270+
String json = mapper.writeValueAsString(input);
271+
assertEquals("{\"non-empty\":\"property\"}", json);
272+
}
273+
274+
// [databind#2592]
275+
public void testAnyGetterConfigIncludeNonEmpty() throws Exception
276+
{
277+
ObjectMapper mapper = new ObjectMapper();
278+
mapper.configOverride(Map.class).setInclude(JsonInclude.Value.construct(
279+
JsonInclude.Include.USE_DEFAULTS,
280+
JsonInclude.Include.NON_EMPTY
281+
));
282+
Bean2592NoAnnotations input = new Bean2592NoAnnotations();
283+
input.add("non-empty", "property");
284+
input.add("empty", "");
285+
input.add("null", null);
286+
String json = mapper.writeValueAsString(input);
287+
assertEquals("{\"non-empty\":\"property\"}", json);
288+
}
199289
}

0 commit comments

Comments
 (0)