1010 */
1111public abstract class InjectableValues
1212{
13+ /**
14+ * @since 2.21
15+ */
16+ public static InjectableValues empty () {
17+ return InjectableValues .Empty .INSTANCE ;
18+ }
19+
1320 /**
1421 * Method called to find value identified by id <code>valueId</code> to
1522 * inject as value of specified property during deserialization, passing
@@ -46,25 +53,120 @@ public abstract Object findInjectableValue(Object valueId, DeserializationContex
4653 throws JsonMappingException ;
4754
4855 /*
49- /**********************************************************
50- /* Standard implementation
51- /**********************************************************
56+ /**********************************************************************
57+ /* Standard implementations
58+ /**********************************************************************
59+ */
60+
61+ /**
62+ * Shared intermediate base class for standard implementations.
63+ *
64+ * @since 2.21
65+ */
66+ public abstract static class Base
67+ extends InjectableValues
68+ implements java .io .Serializable
69+ {
70+ private static final long serialVersionUID = 1L ;
71+
72+ protected String _validateKey (DeserializationContext ctxt , Object valueId ,
73+ BeanProperty forProperty , Object beanInstance )
74+ throws JsonMappingException
75+ {
76+ if (!(valueId instanceof String )) {
77+ throw ctxt .missingInjectableValueException (
78+ String .format (
79+ "Unsupported injectable value id type (%s), expecting String" ,
80+ ClassUtil .classNameOf (valueId )),
81+ valueId , forProperty , beanInstance );
82+ }
83+ return (String ) valueId ;
84+ }
85+
86+ protected Object _handleMissingValue (DeserializationContext ctxt , String key ,
87+ BeanProperty forProperty , Object beanInstance ,
88+ Boolean optionalConfig , Boolean useInputConfig )
89+ throws JsonMappingException
90+ {
91+ // Different defaulting fo "optional" (default to FALSE) and
92+ // "useInput" (default to TRUE)
93+
94+ final boolean optional = Boolean .TRUE .equals (optionalConfig );
95+ final boolean useInput = Boolean .TRUE .equals (useInputConfig );
96+
97+ // [databind#1381]: 14-Nov-2025, tatu: This is a mess: (1) and (2) make sense
98+ // but (3) is debatable. However, for backward compatibility this is what
99+ // passes tests we have.
100+
101+ // Missing ok if:
102+ //
103+ // 1. `optional` is TRUE
104+ // 2. FAIL_ON_UNKNOWN_INJECT_VALUE is disabled
105+ // 3. `useInput` is TRUE and injection is NOT via constructor (implied
106+ // by beanInstance being non-null)
107+ if (optional
108+ || !ctxt .isEnabled (DeserializationFeature .FAIL_ON_UNKNOWN_INJECT_VALUE )
109+ || (useInput && beanInstance != null )
110+ ) {
111+ return null ;
112+ }
113+ throw ctxt .missingInjectableValueException (
114+ String .format ("No injectable value with id '%s' found (for property '%s')" ,
115+ key , forProperty .getName ()),
116+ key , forProperty , beanInstance );
117+ }
118+
119+ /**
120+ * @deprecated in 2.20
121+ */
122+ @ Override
123+ @ Deprecated // since 2.20
124+ public Object findInjectableValue (Object valueId , DeserializationContext ctxt ,
125+ BeanProperty forProperty , Object beanInstance )
126+ throws JsonMappingException
127+ {
128+ return this .findInjectableValue (ctxt , valueId , forProperty , beanInstance ,
129+ null , null );
130+ }
131+ }
132+
133+ /**
134+ * @since 2.21
52135 */
136+ private static final class Empty
137+ extends Base
138+ implements java .io .Serializable
139+ {
140+ private static final long serialVersionUID = 1L ;
141+
142+ final static Empty INSTANCE = new Empty ();
143+
144+ @ Override
145+ public Object findInjectableValue (DeserializationContext ctxt , Object valueId ,
146+ BeanProperty forProperty , Object beanInstance ,
147+ Boolean optional , Boolean useInput )
148+ throws JsonMappingException
149+ {
150+ final String key = _validateKey (ctxt , valueId , forProperty , beanInstance );
151+ return _handleMissingValue (ctxt , key , forProperty , beanInstance ,
152+ optional , useInput );
153+ }
154+ }
53155
54156 /**
55157 * Simple standard implementation which uses a simple Map to
56158 * store values to inject, identified by simple String keys.
57159 */
58160 public static class Std
59- extends InjectableValues
161+ extends Base
60162 implements java .io .Serializable
61163 {
62164 private static final long serialVersionUID = 1L ;
63165
64166 protected final Map <String ,Object > _values ;
65167
66168 public Std () {
67- this (new HashMap <String , Object >());
169+ this (new HashMap <>());
68170 }
69171
70172 public Std (Map <String ,Object > values ) {
@@ -81,48 +183,19 @@ public Std addValue(Class<?> classKey, Object value) {
81183 return this ;
82184 }
83185
84- /**
85- * @since 2.20
86- */
87186 @ Override
88187 public Object findInjectableValue (DeserializationContext ctxt , Object valueId ,
89188 BeanProperty forProperty , Object beanInstance ,
90189 Boolean optional , Boolean useInput )
91190 throws JsonMappingException
92191 {
93- if (!(valueId instanceof String )) {
94- throw ctxt .missingInjectableValueException (
95- String .format (
96- "Unsupported injectable value id type (%s), expecting String" ,
97- ClassUtil .classNameOf (valueId )),
98- valueId , forProperty , beanInstance );
99- }
100- String key = (String ) valueId ;
192+ String key = _validateKey (ctxt , valueId , forProperty , beanInstance );
101193 Object ob = _values .get (key );
102194 if (ob == null && !_values .containsKey (key )) {
103- if (Boolean .FALSE .equals (optional )
104- || ((optional == null )
105- && ctxt .isEnabled (DeserializationFeature .FAIL_ON_UNKNOWN_INJECT_VALUE ))) {
106- throw ctxt .missingInjectableValueException (
107- String .format ("No injectable value with id '%s' found (for property '%s')" ,
108- key , forProperty .getName ()),
109- valueId , forProperty , beanInstance );
110- }
195+ return _handleMissingValue (ctxt , key , forProperty , beanInstance ,
196+ optional , useInput );
111197 }
112198 return ob ;
113199 }
114-
115- /**
116- * @deprecated in 2.20
117- */
118- @ Override
119- @ Deprecated // since 2.20
120- public Object findInjectableValue (Object valueId , DeserializationContext ctxt ,
121- BeanProperty forProperty , Object beanInstance )
122- throws JsonMappingException
123- {
124- return this .findInjectableValue (ctxt , valueId , forProperty , beanInstance ,
125- null , null );
126- }
127200 }
128201}
0 commit comments