JsonValueIgnoresJsonInclude4762Test.java
package tools.jackson.databind.ser;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonValue;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.assertEquals;
// [databind#4762]: `@JsonValue` should honor `@JsonInclude` declared on the annotated accessor
class JsonValueIgnoresJsonInclude4762Test extends DatabindTestUtil
{
static class JsonValueWithInclude {
@JsonValue
@JsonInclude(value = JsonInclude.Include.NON_NULL,
content = JsonInclude.Include.NON_NULL)
public final Map<String, Object> value;
JsonValueWithInclude(Map<String, Object> value) {
this.value = value;
}
}
// Enclosing property with no inclusion override of its own: the accessor-level
// `@JsonInclude` must still be honored when contextualized against a (non-null)
// enclosing `BeanProperty`.
static class Wrapper {
public JsonValueWithInclude wrapped;
Wrapper(JsonValueWithInclude wrapped) {
this.wrapped = wrapped;
}
}
// Enclosing property that overrides content inclusion to ALWAYS: per the established
// precedence ([databind#2822]) the enclosing property configuration wins, re-including
// the null map values that the accessor-level `@JsonInclude` would otherwise drop.
static class WrapperContentAlways {
@JsonInclude(content = JsonInclude.Include.ALWAYS)
public JsonValueWithInclude wrapped;
WrapperContentAlways(JsonValueWithInclude wrapped) {
this.wrapped = wrapped;
}
}
// [databind#4762]: accessor annotations must also be honored when the `@JsonValue`
// value type is `final` (here `BigDecimal`), which takes the static-typing branch of
// `createContextual` rather than the dynamic one.
static class FinalValueWithFormat {
@JsonValue
@JsonFormat(shape = JsonFormat.Shape.STRING)
public final BigDecimal value;
FinalValueWithFormat(BigDecimal value) {
this.value = value;
}
}
// Enclosing property overriding the format back to NUMBER: [databind#2822] precedence
// must hold in the static-typing branch too, so the enclosing override wins.
static class WrapperFormatNumber {
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
public FinalValueWithFormat wrapped;
WrapperFormatNumber(FinalValueWithFormat wrapped) {
this.wrapped = wrapped;
}
}
private final ObjectMapper MAPPER = newJsonMapper();
private Map<String, Object> mapWithNull() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("present", "x");
map.put("missing", null);
return map;
}
@Test
void jsonValueShouldHonorJsonIncludeOnField() throws Exception {
String json = MAPPER.writeValueAsString(new JsonValueWithInclude(mapWithNull()));
// Expected: `@JsonInclude(content = NON_NULL)` on the `@JsonValue` field
// should drop null map values.
assertEquals("{\"present\":\"x\"}", json);
}
// [databind#4762]: accessor-level `@JsonInclude` must be honored even when the
// `@JsonValue` type is nested behind an enclosing property (exercises the
// `_enclosing != null` contextualization path).
@Test
void jsonValueShouldHonorJsonIncludeWhenNested() throws Exception {
String json = MAPPER.writeValueAsString(new Wrapper(new JsonValueWithInclude(mapWithNull())));
assertEquals("{\"wrapped\":{\"present\":\"x\"}}", json);
}
// [databind#2822]: configuration on the enclosing property still takes precedence over
// accessor-level configuration, so a `content = ALWAYS` override re-includes null values.
@Test
void enclosingPropertyInclusionShouldOverrideAccessor() throws Exception {
String json = MAPPER.writeValueAsString(new WrapperContentAlways(new JsonValueWithInclude(mapWithNull())));
assertEquals("{\"wrapped\":{\"present\":\"x\",\"missing\":null}}", json);
}
// [databind#4762]: accessor-level `@JsonFormat` must be honored for a `final` value
// type (static-typing branch), so the `BigDecimal` is written as a String.
@Test
void jsonValueShouldHonorJsonFormatOnFinalAccessor() throws Exception {
String json = MAPPER.writeValueAsString(new FinalValueWithFormat(BigDecimal.ONE));
assertEquals("\"1\"", json);
}
// [databind#2822]: enclosing-property configuration still wins in the static-typing
// branch, so the enclosing `@JsonFormat(NUMBER)` overrides the accessor's STRING.
@Test
void enclosingPropertyFormatShouldOverrideAccessor() throws Exception {
String json = MAPPER.writeValueAsString(new WrapperFormatNumber(new FinalValueWithFormat(BigDecimal.ONE)));
assertEquals("{\"wrapped\":1}", json);
}
}