JsonIncludeMapContentFilter6101Test.java
package tools.jackson.databind.ser.filter;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonInclude;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.assertEquals;
// [databind#6101]: a NON_EMPTY Map property whose entries are all removed by a
// CUSTOM content filter should be treated as empty and omitted, matching the
// behavior for content=NON_EMPTY and the (already correct) write path.
public class JsonIncludeMapContentFilter6101Test extends DatabindTestUtil
{
// equals() returns true for values the content filter should suppress
static class FooFilter {
@Override
public boolean equals(Object other) {
return "foo".equals(other);
}
@Override
public int hashCode() { return 0; }
}
// Statically-typed value serializer (String) -> isEmpty() static-serializer loop
static class Bean {
@JsonInclude(value = JsonInclude.Include.NON_EMPTY,
content = JsonInclude.Include.CUSTOM,
contentFilter = FooFilter.class)
public Map<String, String> stuff = new LinkedHashMap<>();
public Bean add(String key, String value) {
stuff.put(key, value);
return this;
}
}
// Dynamically-resolved value serializer (Object) -> isEmpty() fallback loop
static class DynBean {
@JsonInclude(value = JsonInclude.Include.NON_EMPTY,
content = JsonInclude.Include.CUSTOM,
contentFilter = FooFilter.class)
public Map<String, Object> stuff = new LinkedHashMap<>();
public DynBean add(String key, Object value) {
stuff.put(key, value);
return this;
}
}
private final ObjectMapper MAPPER = newJsonMapper();
// Before the fix this produced {"stuff":{}} because MapSerializer.isEmpty()
// compared the content filter to the whole Map instead of to each entry value.
@Test
public void mapEmptyAfterContentFilter() throws Exception {
Bean bean = new Bean().add("a", "foo").add("b", "foo");
assertEquals("{}", MAPPER.writeValueAsString(bean));
}
@Test
public void mapKeepsSurvivingEntries() throws Exception {
Bean bean = new Bean().add("a", "foo").add("b", "keep");
assertEquals("""
{"stuff":{"b":"keep"}}""",
MAPPER.writeValueAsString(bean));
}
@Test
public void genuinelyEmptyMapOmitted() throws Exception {
assertEquals("{}", MAPPER.writeValueAsString(new Bean()));
}
// Same bug on the fallback loop taken when the value serializer is not
// statically known (Map<String,Object>).
@Test
public void dynamicValueMapEmptyAfterContentFilter() throws Exception {
DynBean bean = new DynBean().add("a", "foo").add("b", "foo");
assertEquals("{}", MAPPER.writeValueAsString(bean));
}
@Test
public void dynamicValueMapKeepsSurvivingEntries() throws Exception {
DynBean bean = new DynBean().add("a", "foo").add("b", "keep");
assertEquals("""
{"stuff":{"b":"keep"}}""",
MAPPER.writeValueAsString(bean));
}
}