Skip to content

Commit 84fad0d

Browse files
committed
Merge branch '2.x' into 3.x
2 parents b960382 + f15b71d commit 84fad0d

File tree

13 files changed

+248
-40
lines changed

13 files changed

+248
-40
lines changed

release-notes/CREDITS-2.x

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1215,10 +1215,13 @@ Oleg Chtchoukine (oshatrk@github)
12151215
to incorrect output
12161216
(2.11.1)
12171217

1218-
Joshua Shannon (retrodaredevil@github)
1218+
Lavender Shannon (retrodaredevil@github)
12191219
* Reported, contributed fix for #2785: Polymorphic subtypes not registering on copied
12201220
ObjectMapper (2.11.1)
12211221
(2.11.2)
1222+
* Requested #3072: Allow specifying `@JacksonInject` does not fail when there's no
1223+
corresponding value
1224+
(2.20.0)
12221225
12231226
Daniel Hrabovcak (TheSpiritXIII@github)
12241227
* Reported #2796: `TypeFactory.constructType()` does not take `TypeBindings` correctly
@@ -1940,3 +1943,8 @@ Will Paul (@dropofwill)
19401943
Ryan Schmitt (@rschmitt)
19411944
* Contributed #5099: Fix regression in `ObjectNode.with()`
19421945
(2.19.0)
1946+
1947+
Giulio Longfils (@giulong)
1948+
* Contributed #3072: Allow specifying `@JacksonInject` does not fail when there's no
1949+
corresponding value
1950+
(2.20.0)

release-notes/VERSION-2.x

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Project: jackson-databind
66

77
2.20.0 (not yet released)
88

9+
#3072: Allow specifying `@JacksonInject` does not fail when there's no
10+
corresponding value
11+
(requested by Lavender S)
12+
(contributed by Giulio L)
913
#4136: Drop deprecated (in 2.12) `PropertyNamingStrategy` implementations
1014
from 2.20
1115
#5103: Use `writeStartObject(Object forValue, int size)` for `ObjectNode`

src/main/java/tools/jackson/databind/DeserializationContext.java

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.text.ParseException;
55
import java.util.*;
66

7+
import com.fasterxml.jackson.annotation.JacksonInject;
78
import com.fasterxml.jackson.annotation.JsonFormat;
89
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
910
import com.fasterxml.jackson.annotation.ObjectIdResolver;
@@ -14,25 +15,12 @@
1415
import tools.jackson.core.type.ResolvedType;
1516
import tools.jackson.core.type.TypeReference;
1617
import tools.jackson.core.util.JacksonFeatureSet;
17-
import tools.jackson.databind.cfg.CoercionAction;
18-
import tools.jackson.databind.cfg.CoercionInputShape;
19-
import tools.jackson.databind.cfg.ContextAttributes;
20-
import tools.jackson.databind.cfg.DatatypeFeature;
21-
import tools.jackson.databind.cfg.DatatypeFeatures;
18+
import tools.jackson.databind.cfg.*;
2219
import tools.jackson.databind.deser.*;
2320
import tools.jackson.databind.deser.impl.ObjectIdReader;
2421
import tools.jackson.databind.deser.impl.TypeWrappedDeserializer;
25-
import tools.jackson.databind.exc.InvalidDefinitionException;
26-
import tools.jackson.databind.exc.InvalidFormatException;
27-
import tools.jackson.databind.exc.InvalidTypeIdException;
28-
import tools.jackson.databind.exc.MismatchedInputException;
29-
import tools.jackson.databind.exc.UnrecognizedPropertyException;
30-
import tools.jackson.databind.exc.ValueInstantiationException;
31-
import tools.jackson.databind.introspect.Annotated;
32-
import tools.jackson.databind.introspect.AnnotatedClass;
33-
import tools.jackson.databind.introspect.AnnotatedMember;
34-
import tools.jackson.databind.introspect.BeanPropertyDefinition;
35-
import tools.jackson.databind.introspect.ClassIntrospector;
22+
import tools.jackson.databind.exc.*;
23+
import tools.jackson.databind.introspect.*;
3624
import tools.jackson.databind.jsontype.TypeDeserializer;
3725
import tools.jackson.databind.jsontype.TypeIdResolver;
3826
import tools.jackson.databind.node.JsonNodeFactory;
@@ -482,13 +470,19 @@ public final boolean isEnabled(StreamReadCapability cap) {
482470
public final JsonParser getParser() { return _parser; }
483471

484472
public final Object findInjectableValue(Object valueId,
485-
BeanProperty forProperty, Object beanInstance)
473+
BeanProperty forProperty, Object beanInstance, Boolean optional)
486474
{
487475
if (_injectableValues == null) {
476+
// `optional` comes from property annotation (if any); has precedence
477+
// over global setting.
478+
if (Boolean.TRUE.equals(optional)
479+
|| (optional == null && !isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_INJECT_VALUE))) {
480+
return JacksonInject.Value.empty();
481+
}
488482
return reportBadDefinition(ClassUtil.classOf(valueId), String.format(
489-
"No 'injectableValues' configured, cannot inject value with id [%s]", valueId));
483+
"No 'injectableValues' configured, cannot inject value with id '%s'", valueId));
490484
}
491-
return _injectableValues.findInjectableValue(valueId, this, forProperty, beanInstance);
485+
return _injectableValues.findInjectableValue(valueId, this, forProperty, beanInstance, optional);
492486
}
493487

494488
/**

src/main/java/tools/jackson/databind/DeserializationFeature.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package tools.jackson.databind;
22

3+
import com.fasterxml.jackson.annotation.JacksonInject;
4+
35
import tools.jackson.databind.cfg.ConfigFeature;
46
import tools.jackson.databind.cfg.MapperBuilder;
57

@@ -234,12 +236,19 @@ public enum DeserializationFeature implements ConfigFeature
234236
* for binding the full value, and nothing more (except for possible ignorable
235237
* white space or comments, if supported by data format).
236238
*<p>
239+
<<<<<<< HEAD:src/main/java/tools/jackson/databind/DeserializationFeature.java
237240
* NOTE: this feature should usually be disabled when reading from
238241
* {@link java.io.DataInput}, since it cannot detect end-of-input efficiently
239242
* (but by throwing an {@link java.io.IOException}). Disabling is NOT done
240243
* automatically by Jackson: users are recommended to disable it.
241244
*<p>
242245
* Feature is enabled by default as of Jackson 3.0 (in 2.x it was disabled).
246+
=======
247+
* Feature is disabled by default (so that no check is made for possible trailing
248+
* token(s)) for backwards-compatibility reasons.
249+
*
250+
* @since 2.9
251+
>>>>>>> 2.x:src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
243252
*/
244253
FAIL_ON_TRAILING_TOKENS(true),
245254

@@ -301,6 +310,21 @@ public enum DeserializationFeature implements ConfigFeature
301310
*/
302311
FAIL_ON_UNEXPECTED_VIEW_PROPERTIES(false),
303312

313+
/**
314+
* Feature that determines the handling of injected properties during deserialization.
315+
*<p>
316+
* When enabled, if an injected property without matching value is encountered
317+
* during deserialization, an exception is thrown.
318+
* When disabled, no exception is thrown.
319+
* See {@link JacksonInject#optional()} for per-property override
320+
* of this setting.
321+
*<p>
322+
* This feature is enabled by default to maintain backwards-compatibility.
323+
*
324+
* @see JacksonInject#optional()
325+
*/
326+
FAIL_ON_UNKNOWN_INJECT_VALUE(true),
327+
304328
/*
305329
/**********************************************************************
306330
/* Structural conversion features

src/main/java/tools/jackson/databind/InjectableValues.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ public abstract class InjectableValues
2525
* @param forProperty Bean property in which value is to be injected
2626
* @param beanInstance Bean instance that contains property to inject,
2727
* if available; null if bean has not yet been constructed.
28+
* @param optional Flag used for configuring the behavior when the value
29+
* to inject is not found
2830
*/
2931
public abstract Object findInjectableValue(Object valueId, DeserializationContext ctxt,
30-
BeanProperty forProperty, Object beanInstance);
32+
BeanProperty forProperty, Object beanInstance, Boolean optional);
3133

3234
/*
3335
/**********************************************************
@@ -75,7 +77,7 @@ public Std snapshot() {
7577

7678
@Override
7779
public Object findInjectableValue(Object valueId, DeserializationContext ctxt,
78-
BeanProperty forProperty, Object beanInstance)
80+
BeanProperty forProperty, Object beanInstance, Boolean optional)
7981
{
8082
if (!(valueId instanceof String)) {
8183
ctxt.reportBadDefinition(ClassUtil.classOf(valueId),
@@ -86,7 +88,13 @@ public Object findInjectableValue(Object valueId, DeserializationContext ctxt,
8688
String key = (String) valueId;
8789
Object ob = _values.get(key);
8890
if (ob == null && !_values.containsKey(key)) {
89-
throw new IllegalArgumentException("No injectable value with id '"+key+"' found (for property '"+forProperty.getName()+"')");
91+
if (Boolean.FALSE.equals(optional)
92+
|| ((optional == null)
93+
&& ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_INJECT_VALUE))) {
94+
return ctxt.reportBadDefinition(ClassUtil.classOf(valueId), String.format(
95+
"No injectable value with id '" + key + "' " +
96+
"found (for property '" + forProperty.getName() + "')"));
97+
}
9098
}
9199
return ob;
92100
}

src/main/java/tools/jackson/databind/deser/BeanDeserializerBuilder.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,10 @@ public void addBackReferenceProperty(String referenceName, SettableBeanProperty
240240

241241
public void addInjectable(PropertyName propName, JavaType propType,
242242
Annotations contextAnnotations, AnnotatedMember member,
243-
Object valueId)
243+
Object valueId, Boolean optional)
244244
{
245245
if (_injectables == null) {
246-
_injectables = new ArrayList<ValueInjector>();
246+
_injectables = new ArrayList<>();
247247
}
248248
if ( _config.canOverrideAccessModifiers()) {
249249
try {
@@ -252,7 +252,7 @@ public void addInjectable(PropertyName propName, JavaType propType,
252252
_handleBadAccess(e);
253253
}
254254
}
255-
_injectables.add(new ValueInjector(propName, propType, member, valueId));
255+
_injectables.add(new ValueInjector(propName, propType, member, valueId, optional));
256256
}
257257

258258
/**
@@ -262,15 +262,13 @@ public void addInjectable(PropertyName propName, JavaType propType,
262262
public void addIgnorable(String propName)
263263
{
264264
if (_ignorableProps == null) {
265-
_ignorableProps = new HashSet<String>();
265+
_ignorableProps = new HashSet<>();
266266
}
267267
_ignorableProps.add(propName);
268268
}
269269

270270
/**
271271
* Method that will add property name as one of the properties that will be included.
272-
*
273-
* @since 2.12
274272
*/
275273
public void addIncludable(String propName)
276274
{

src/main/java/tools/jackson/databind/deser/BeanDeserializerFactory.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package tools.jackson.databind.deser;
22

3-
import java.lang.reflect.Modifier;
43
import java.util.*;
54

65
import com.fasterxml.jackson.annotation.*;
@@ -774,11 +773,16 @@ protected void addInjectables(DeserializationContext ctxt,
774773
{
775774
Map<Object, AnnotatedMember> raw = beanDescRef.get().findInjectables();
776775
if (raw != null) {
776+
final AnnotationIntrospector introspector = ctxt.getAnnotationIntrospector();
777+
777778
for (Map.Entry<Object, AnnotatedMember> entry : raw.entrySet()) {
778779
AnnotatedMember m = entry.getValue();
780+
final JacksonInject.Value injectableValue = introspector.findInjectableValue(ctxt.getConfig(), m);
781+
final Boolean optional = injectableValue == null ? null : injectableValue.getOptional();
782+
779783
builder.addInjectable(PropertyName.construct(m.getName()),
780784
m.getType(),
781-
beanDescRef.getClassAnnotations(), m, entry.getKey());
785+
beanDescRef.getClassAnnotations(), m, entry.getKey(), optional);
782786
}
783787
}
784788
}

src/main/java/tools/jackson/databind/deser/bean/PropertyValueBuffer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ protected Object _findMissing(SettableBeanProperty prop) throws DatabindExceptio
258258
Object injectableValueId = prop.getInjectableValueId();
259259
if (injectableValueId != null) {
260260
return _context.findInjectableValue(prop.getInjectableValueId(),
261-
prop, null);
261+
prop, null, null);
262262
}
263263
// Second: required?
264264
if (prop.isRequired()) {

src/main/java/tools/jackson/databind/deser/impl/ValueInjector.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package tools.jackson.databind.deser.impl;
22

3+
import com.fasterxml.jackson.annotation.JacksonInject;
4+
35
import tools.jackson.core.JacksonException;
46
import tools.jackson.databind.*;
57
import tools.jackson.databind.introspect.AnnotatedMember;
@@ -20,22 +22,31 @@ public class ValueInjector
2022
*/
2123
protected final Object _valueId;
2224

25+
/**
26+
* Flag used for configuring the behavior when the value to inject is not found.
27+
*/
28+
protected final Boolean _optional;
29+
2330
public ValueInjector(PropertyName propName, JavaType type,
24-
AnnotatedMember mutator, Object valueId)
31+
AnnotatedMember mutator, Object valueId, Boolean optional)
2532
{
2633
super(propName, type, null, mutator, PropertyMetadata.STD_OPTIONAL);
2734
_valueId = valueId;
35+
_optional = optional;
2836
}
2937

3038
public Object findValue(DeserializationContext context, Object beanInstance)
3139
throws JacksonException
3240
{
33-
return context.findInjectableValue(_valueId, this, beanInstance);
41+
return context.findInjectableValue(_valueId, this, beanInstance, _optional);
3442
}
3543

3644
public void inject(DeserializationContext context, Object beanInstance)
3745
throws JacksonException
3846
{
39-
_member.setValue(beanInstance, findValue(context, beanInstance));
47+
final Object value = findValue(context, beanInstance);
48+
if (!JacksonInject.Value.empty().equals(value)) {
49+
_member.setValue(beanInstance, value);
50+
}
4051
}
41-
}
52+
}

src/main/java/tools/jackson/databind/deser/std/StdValueInstantiator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,8 @@ private Object _createUsingDelegate(AnnotatedWithParams delegateCreator,
629629
if (prop == null) { // delegate
630630
args[i] = delegate;
631631
} else { // nope, injectable:
632-
args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null);
632+
// 09-May-2025, tatu: Not sure where to get "optional" (last arg) value...
633+
args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null, null);
633634
}
634635
}
635636
// and then try calling with full set of arguments

0 commit comments

Comments
 (0)