19
19
import java .beans .ConstructorProperties ;
20
20
import java .util .Map ;
21
21
import java .util .Optional ;
22
+ import java .util .Set ;
22
23
23
24
import jakarta .validation .constraints .NotNull ;
24
25
import org .junit .jupiter .api .Test ;
25
26
26
27
import org .springframework .core .ResolvableType ;
27
28
import org .springframework .format .support .DefaultFormattingConversionService ;
29
+ import org .springframework .lang .Nullable ;
28
30
import org .springframework .util .Assert ;
29
31
30
32
import static org .assertj .core .api .Assertions .assertThat ;
@@ -76,6 +78,17 @@ void dataClassBindingWithMissingParameter() {
76
78
assertThat (bindingResult .getFieldValue ("param3" )).isNull ();
77
79
}
78
80
81
+ @ Test // gh-31821
82
+ void dataClassBindingWithNestedOptionalParameterWithMissingParameter () {
83
+ MapValueResolver valueResolver = new MapValueResolver (Map .of ("param1" , "value1" ));
84
+ DataBinder binder = initDataBinder (NestedDataClass .class );
85
+ binder .construct (valueResolver );
86
+
87
+ NestedDataClass dataClass = getTarget (binder );
88
+ assertThat (dataClass .param1 ()).isEqualTo ("value1" );
89
+ assertThat (dataClass .nestedParam2 ()).isNull ();
90
+ }
91
+
79
92
@ Test
80
93
void dataClassBindingWithConversionError () {
81
94
MapValueResolver valueResolver = new MapValueResolver (Map .of ("param1" , "value1" , "param2" , "x" ));
@@ -90,7 +103,7 @@ void dataClassBindingWithConversionError() {
90
103
}
91
104
92
105
@ SuppressWarnings ("SameParameterValue" )
93
- private static DataBinder initDataBinder (Class <DataClass > targetType ) {
106
+ private static DataBinder initDataBinder (Class <? > targetType ) {
94
107
DataBinder binder = new DataBinder (null );
95
108
binder .setTargetType (ResolvableType .forClass (targetType ));
96
109
binder .setConversionService (new DefaultFormattingConversionService ());
@@ -137,17 +150,45 @@ public int param3() {
137
150
}
138
151
139
152
153
+ private static class NestedDataClass {
154
+
155
+ private final String param1 ;
156
+
157
+ @ Nullable
158
+ private final DataClass nestedParam2 ;
159
+
160
+ public NestedDataClass (String param1 , @ Nullable DataClass nestedParam2 ) {
161
+ this .param1 = param1 ;
162
+ this .nestedParam2 = nestedParam2 ;
163
+ }
164
+
165
+ public String param1 () {
166
+ return this .param1 ;
167
+ }
168
+
169
+ @ Nullable
170
+ public DataClass nestedParam2 () {
171
+ return this .nestedParam2 ;
172
+ }
173
+ }
174
+
175
+
140
176
private static class MapValueResolver implements DataBinder .ValueResolver {
141
177
142
- private final Map <String , Object > values ;
178
+ private final Map <String , Object > map ;
143
179
144
- private MapValueResolver (Map <String , Object > values ) {
145
- this .values = values ;
180
+ private MapValueResolver (Map <String , Object > map ) {
181
+ this .map = map ;
146
182
}
147
183
148
184
@ Override
149
185
public Object resolveValue (String name , Class <?> type ) {
150
- return values .get (name );
186
+ return map .get (name );
187
+ }
188
+
189
+ @ Override
190
+ public Set <String > getNames () {
191
+ return this .map .keySet ();
151
192
}
152
193
}
153
194
0 commit comments