Skip to content

Commit 41f8ca1

Browse files
committed
Implement #781
1 parent 47661f2 commit 41f8ca1

File tree

5 files changed

+164
-1
lines changed

5 files changed

+164
-1
lines changed

release-notes/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Project: jackson-databind
3535
#765: `SimpleType.withStaticTyping()` impl incorrect
3636
#769: Fix `JacksonAnnotationIntrospector.findDeserializer` to return `Object` (as per
3737
`AnnotationIntrospector`); similarly for other `findXxx(De)Serializer(...)` methods
38+
#781: Support handling of `@JsonProperty.required` for Creator methods
3839
- Remove old cglib compatibility tests; cause problems in Eclipse
3940

4041
2.5.4 (not yet released)

src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java

+21
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,27 @@ public enum DeserializationFeature implements ConfigFeature
188188
*/
189189
FAIL_ON_UNRESOLVED_OBJECT_IDS(true),
190190

191+
/**
192+
* Feature that determines what happens if one or more Creator properties (properties
193+
* bound to parameters of Creator method (constructor or static factory method))
194+
* are missing value to bind to from content.
195+
* If enabled, such missing values result in a {@link JsonMappingException} being
196+
* thrown with information on the first one (by index) of missing properties.
197+
* If disabled, and if property is NOT marked as required,
198+
* missing Creator properties are filled
199+
* with <code>null values</code> provided by deserializer for the type of parameter
200+
* (usually null for Object types, and default value for primitives; but redefinable
201+
* via custom deserializers).
202+
*<p>
203+
* Note that having an injectable value counts as "not missing".
204+
*<p>
205+
* Feature is disabled by default, so that no exception is thrown for missing creator
206+
* property values, unless they are explicitly marked as `required`.
207+
*
208+
* @since 2.5
209+
*/
210+
FAIL_ON_MISSING_CREATOR_PROPERTIES(false),
211+
191212
/**
192213
* Feature that determines whether Jackson code should catch
193214
* and wrap {@link Exception}s (but never {@link Error}s!)

src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import com.fasterxml.jackson.core.JsonParser;
77
import com.fasterxml.jackson.databind.DeserializationContext;
8+
import com.fasterxml.jackson.databind.DeserializationFeature;
89
import com.fasterxml.jackson.databind.JsonDeserializer;
910
import com.fasterxml.jackson.databind.JsonMappingException;
1011
import com.fasterxml.jackson.databind.deser.SettableAnyProperty;
@@ -144,7 +145,10 @@ protected Object _findMissing(SettableBeanProperty prop) throws JsonMappingExcep
144145
throw _context.mappingException(String.format("Missing required creator property '%s' (index %d)",
145146
prop.getName(), prop.getCreatorIndex()));
146147
}
147-
148+
if (_context.isEnabled(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)) {
149+
throw _context.mappingException(String.format("Missing creator property '%s' (index %d); DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES enabled",
150+
prop.getName(), prop.getCreatorIndex()));
151+
}
148152
// Third: default value
149153
JsonDeserializer<Object> deser = prop.getValueDeserializer();
150154
return deser.getNullValue(_context);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.fasterxml.jackson.databind.creators;
2+
3+
import com.fasterxml.jackson.annotation.*;
4+
5+
import com.fasterxml.jackson.databind.*;
6+
7+
// Test(s) for "big" creators; ones with at least 32 arguments (sic!).
8+
// Needed because codepaths diverge wrt handling of bitset
9+
public class BigCreatorTest extends BaseMapTest
10+
{
11+
static class Biggie {
12+
final int[] stuff;
13+
14+
@JsonCreator
15+
public Biggie(
16+
@JsonProperty("v1") int v1, @JsonProperty("v2") int v2,
17+
@JsonProperty("v3") int v3, @JsonProperty("v4") int v4,
18+
@JsonProperty("v5") int v5, @JsonProperty("v6") int v6,
19+
@JsonProperty("v7") int v7, @JsonProperty("v8") int v8,
20+
@JsonProperty("v9") int v9, @JsonProperty("v10") int v10,
21+
@JsonProperty("v11") int v11, @JsonProperty("v12") int v12,
22+
@JsonProperty("v13") int v13, @JsonProperty("v14") int v14,
23+
@JsonProperty("v15") int v15, @JsonProperty("v16") int v16,
24+
@JsonProperty("v17") int v17, @JsonProperty("v18") int v18,
25+
@JsonProperty("v19") int v19, @JsonProperty("v20") int v20,
26+
@JsonProperty("v21") int v21, @JsonProperty("v22") int v22,
27+
@JsonProperty("v23") int v23, @JsonProperty("v24") int v24,
28+
@JsonProperty("v25") int v25, @JsonProperty("v26") int v26,
29+
@JsonProperty("v27") int v27, @JsonProperty("v28") int v28,
30+
@JsonProperty("v29") int v29, @JsonProperty("v30") int v30,
31+
@JsonProperty("v31") int v31, @JsonProperty("v32") int v32,
32+
@JsonProperty("v33") int v33, @JsonProperty("v34") int v34,
33+
@JsonProperty("v35") int v35, @JsonProperty("v36") int v36,
34+
@JsonProperty("v37") int v37, @JsonProperty("v38") int v38,
35+
@JsonProperty("v39") int v39, @JsonProperty("v40") int v40
36+
) {
37+
stuff = new int[] {
38+
v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
39+
v11, v12, v13, v14, v15, v16, v17, v18, v19, v20,
40+
v21, v22, v23, v24, v25, v26, v27, v28, v29, v30,
41+
v31, v32, v33, v34, v35, v36, v37, v38, v39, v40,
42+
};
43+
}
44+
}
45+
46+
private final ObjectReader BIGGIE_READER = objectReader(Biggie.class);
47+
48+
public void testBigPartial() throws Exception
49+
{
50+
Biggie value = BIGGIE_READER.readValue(aposToQuotes(
51+
"{'v7':7, 'v8':8,'v29':29, 'v35':35}"
52+
));
53+
int[] stuff = value.stuff;
54+
for (int i = 0; i < stuff.length; ++i) {
55+
int exp;
56+
57+
switch (i) {
58+
case 6: // These are off-by-one...
59+
case 7:
60+
case 28:
61+
case 34:
62+
exp = i+1;
63+
break;
64+
default:
65+
exp = 0;
66+
}
67+
assertEquals("Entry #"+i, exp, stuff[i]);
68+
}
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.fasterxml.jackson.databind.creators;
2+
3+
import com.fasterxml.jackson.annotation.*;
4+
5+
import com.fasterxml.jackson.databind.*;
6+
7+
public class RequiredCreatorTest extends BaseMapTest
8+
{
9+
static class FascistPoint {
10+
int x, y;
11+
12+
@JsonCreator
13+
public FascistPoint(@JsonProperty(value="x", required=true) int x,
14+
@JsonProperty(value="y", required=false) int y)
15+
{
16+
this.x = x;
17+
this.y = y;
18+
}
19+
}
20+
21+
private final ObjectReader POINT_READER = objectMapper().reader(FascistPoint.class);
22+
23+
public void testRequiredAnnotatedParam() throws Exception
24+
{
25+
FascistPoint p;
26+
27+
// First: fine if both params passed
28+
p = POINT_READER.readValue(aposToQuotes("{'y':2,'x':1}"));
29+
assertEquals(1, p.x);
30+
assertEquals(2, p.y);
31+
p = POINT_READER.readValue(aposToQuotes("{'x':3,'y':4}"));
32+
assertEquals(3, p.x);
33+
assertEquals(4, p.y);
34+
35+
// also fine if 'y' is MIA
36+
p = POINT_READER.readValue(aposToQuotes("{'x':3}"));
37+
assertEquals(3, p.x);
38+
assertEquals(0, p.y);
39+
40+
// but not so good if 'x' missing
41+
try {
42+
POINT_READER.readValue(aposToQuotes("{'y':3}"));
43+
fail("Should not pass");
44+
} catch (JsonMappingException e) {
45+
verifyException(e, "Missing required creator property 'x' (index 0)");
46+
}
47+
}
48+
49+
public void testRequiredGloballyParam() throws Exception
50+
{
51+
FascistPoint p;
52+
53+
// as per above, ok to miss 'y' with default settings:
54+
p = POINT_READER.readValue(aposToQuotes("{'x':2}"));
55+
assertEquals(2, p.x);
56+
assertEquals(0, p.y);
57+
58+
// but not if global checks desired
59+
ObjectReader r = POINT_READER.with(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES);
60+
try {
61+
r.readValue(aposToQuotes("{'x':6}"));
62+
fail("Should not pass");
63+
} catch (JsonMappingException e) {
64+
verifyException(e, "Missing creator property 'y' (index 1)");
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)