Describe the bug
@JsonIgnoreProperties is not applied to Creator properties on three of the property-based-Creator deserialization paths, so a name the annotation lists still gets deserialized and passed to the constructor or builder.
BeanDeserializer checks the ignore set before assigning a Creator property (the [databind#4629] guard, needed because a type with a property-based @JsonCreator resolves a valid creatorProp and so never reaches the by-name ignore check further down the loop). That guard is present in deserializeUsingPropertyBased and deserializeUsingPropertyBasedWithUnwrapped, but not in deserializeUsingPropertyBasedWithExternalTypeId. BuilderBasedDeserializer does not have it on any of its Creator loops (_deserializeUsingPropertyBased, deserializeUsingPropertyBasedWithUnwrapped).
So the ignore configuration works for a plain POJO or record, and stops working for the same type once it is built through a @JsonPOJOBuilder, or once it has an @JsonTypeInfo(include = EXTERNAL_PROPERTY) property.
Version Information
3.2.2-SNAPSHOT (also reproduces on 3.3.0-SNAPSHOT). 3.1 has a narrower, record-only form of the guard, so its behavior differs.
Reproduction
Builder case:
@JsonDeserialize(builder = BuiltValue.Builder.class)
static class BuiltValue {
public final int id;
public final String secret;
BuiltValue(int id, String secret) { this.id = id; this.secret = secret; }
@JsonPOJOBuilder(withPrefix = "")
static class Builder {
private final int id;
private final String secret;
@JsonCreator
public Builder(@JsonProperty("id") int id, @JsonProperty("secret") String secret) {
this.id = id;
this.secret = secret;
}
public BuiltValue build() { return new BuiltValue(id, secret); }
}
}
static class BuilderWrapper {
@JsonIgnoreProperties("secret")
public BuiltValue child;
}
BuilderWrapper w = MAPPER.readValue(
"{\"child\":{\"id\":13,\"secret\":\"leaked\"}}", BuilderWrapper.class);
// w.child.secret is "leaked"; expected null
External type id case:
static abstract class Animal { public String name; }
static class Dog extends Animal { }
static class ExtTypeValue {
public final String secret;
public final Animal value;
@JsonCreator
public ExtTypeValue(@JsonProperty("secret") String secret,
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog") })
@JsonProperty("value") Animal value) {
this.secret = secret;
this.value = value;
}
}
static class ExtTypeWrapper {
@JsonIgnoreProperties("secret")
public ExtTypeValue child;
}
ExtTypeWrapper w = MAPPER.readValue(
"{\"child\":{\"secret\":\"leaked\",\"type\":\"dog\",\"value\":{\"name\":\"Rex\"}}}",
ExtTypeWrapper.class);
// w.child.secret is "leaked"; expected null
The same thing happens when the builder has @JsonUnwrapped properties, which routes through BuilderBasedDeserializer.deserializeUsingPropertyBasedWithUnwrapped.
Expected behavior
secret should be left unset in all three cases, matching what the plain property-based Creator path already does.
Additional context
The practical impact is that the annotation silently stops filtering input for these shapes, so a property an application deliberately excluded from binding is accepted from the incoming document. Happy to open a PR adding the same guard to the three loops.
Describe the bug
@JsonIgnorePropertiesis not applied to Creator properties on three of the property-based-Creator deserialization paths, so a name the annotation lists still gets deserialized and passed to the constructor or builder.BeanDeserializerchecks the ignore set before assigning a Creator property (the[databind#4629]guard, needed because a type with a property-based@JsonCreatorresolves a validcreatorPropand so never reaches the by-name ignore check further down the loop). That guard is present indeserializeUsingPropertyBasedanddeserializeUsingPropertyBasedWithUnwrapped, but not indeserializeUsingPropertyBasedWithExternalTypeId.BuilderBasedDeserializerdoes not have it on any of its Creator loops (_deserializeUsingPropertyBased,deserializeUsingPropertyBasedWithUnwrapped).So the ignore configuration works for a plain POJO or record, and stops working for the same type once it is built through a
@JsonPOJOBuilder, or once it has an@JsonTypeInfo(include = EXTERNAL_PROPERTY)property.Version Information
3.2.2-SNAPSHOT (also reproduces on 3.3.0-SNAPSHOT). 3.1 has a narrower, record-only form of the guard, so its behavior differs.
Reproduction
Builder case:
External type id case:
The same thing happens when the builder has
@JsonUnwrappedproperties, which routes throughBuilderBasedDeserializer.deserializeUsingPropertyBasedWithUnwrapped.Expected behavior
secretshould be left unset in all three cases, matching what the plain property-based Creator path already does.Additional context
The practical impact is that the annotation silently stops filtering input for these shapes, so a property an application deliberately excluded from binding is accepted from the incoming document. Happy to open a PR adding the same guard to the three loops.