FasterXML / FasterXML/jackson-databind
`@JacksonInject(useInput = OptBoolean.FALSE)` ignored for Field and Setter properties
- Dominant language
- Java
- Stars
- 3.7k
- Forks
- 1.5k
- Avg merge
- 3d 6h
- Merged PRs (30d)
- 28
Description
### Search before asking
- [X] I searched in the [issues](https://github.com/FasterXML/jackson-databind/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
```java
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:
```java
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.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with SettableBeanProperty.getInjectionDefinition(), ValueInjector.injectValues(), and the injection-only checks described in BeanDeserializer and related deserializers. Read JacksonInject1381Test first, then cover Field- and Setter-backed properties, including the listed array, builder, unwrapped, and updating paths. Done means explicit FALSE preserves the injected value while DEFAULT and TRUE retain current behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100