Skip to content

@JacksonInject(useInput = OptBoolean.FALSE) ignored for Field and Setter properties #6201

Description

@aysha-afrah26

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

@JacksonInject documents useInput = OptBoolean.FALSE as "input value (if any) will be ignored", and the @return javadoc spells it out further: "OptBoolean.FALSE if injected value will always be used regardless of input".

That holds for Creator properties. CreatorProperty.isInjectionOnly() reports it and the property-based-Creator loops act on it (the [databind#1381] checks in BeanDeserializer, BuilderBasedDeserializer, BeanAsArrayDeserializer and BeanAsArrayBuilderDeserializer), with PropertyValueBuffer._inject() supplying the value afterwards.

It does not hold for a property backed by a Field or a Setter. SettableBeanProperty.getInjectionDefinition() returns null for those, so isInjectionOnly() is always false and the property stays in the settable set. Injection for them goes through ValueInjector instead, and injectValues() runs right after the bean is constructed, before any property is read, so a matching name in the document just overwrites the injected value.

The practical consequence is for code that uses @JacksonInject to pin a server-side value onto a POJO, say a tenant id, a principal, or a request id: with useInput = OptBoolean.FALSE the caller has asked for that value to be non-overridable, and the document overrides it anyway.

JacksonInject1381Test already asserts the intended behavior ("input YES, injectable YES, useInput DEFAULT|FALSE => injected"), but every fixture in it reaches the value through a Creator, so the Field/Setter path is not covered.

Version Information

3.1, 3.2, 3.x (also 2.22.2)

Reproduction

public class Doc {
    @JacksonInject(value = "tenant", useInput = OptBoolean.FALSE)
    public String tenant = "unset";

    public String title = "";
}

ObjectMapper mapper = JsonMapper.builder()
        .injectableValues(new InjectableValues.Std().addValue("tenant", "tenant-42"))
        .build();

Doc doc = mapper.readValue("{\"tenant\":\"tenant-99\",\"title\":\"x\"}", Doc.class);

System.out.println(doc.tenant); // prints "tenant-99", expected "tenant-42"

The same holds for a @JacksonInject-annotated setter, for @JsonFormat(shape = Shape.ARRAY) types, for @JsonPOJOBuilder builders, for @JsonUnwrapped values, and on the readerForUpdating() path.

Moving the same declaration onto a Creator parameter gives the documented result:

public class Doc {
    public final String tenant;
    public final String title;

    @JsonCreator
    public Doc(@JacksonInject(value = "tenant", useInput = OptBoolean.FALSE) String tenant,
            @JsonProperty("title") String title) {
        this.tenant = tenant;
        this.title = title;
    }
}
// doc.tenant is "tenant-42"

Expected behavior

A Field- or Setter-backed property annotated with @JacksonInject(useInput = OptBoolean.FALSE) should keep the injected value and drop any matching value from input, the same as the Creator case.

Only the explicit OptBoolean.FALSE setting is at issue here. DEFAULT and TRUE should keep behaving as they do now.

Additional context

Related: #1381 (where useInput handling was introduced) and #2678.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions