Skip to content

Commit ff441e8

Browse files
committed
Fixed #1505
1 parent 4d34006 commit ff441e8

File tree

5 files changed

+240
-9
lines changed

5 files changed

+240
-9
lines changed

release-notes/CREDITS

+4
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,10 @@ Frédéric Camblor (fcamblor@github)
579579
* Reported #1451: Type parameter not passed by `ObjectWriter` if serializer pre-fetch disabled
580580
(2.8.6)
581581

582+
Stephan Schroevers (Stephan202@github)
583+
* Reported #1505: @JsonEnumDefaultValue should take precedence over FAIL_ON_NUMBERS_FOR_ENUMS
584+
(2.8.7)
585+
582586
Connor Kuhn (ckuhn@github)
583587
* Contributed #1341: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY
584588
(2.9.0)

release-notes/VERSION

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ Project: jackson-databind
66
2.8.7 (not yet released)
77

88
#935: `@JsonProperty(access = Access.READ_ONLY)` - unexpected behaviour
9-
#1317: '@JsonIgnore' annotation not working with creator properties, serializatio
9+
#1317: '@JsonIgnore' annotation not working with creator properties, serialization
10+
#1505: @JsonEnumDefaultValue should take precedence over FAIL_ON_NUMBERS_FOR_ENUMS
11+
(suggested by Stephan S)
1012

1113
2.8.6 (12-Jan-2017)
1214

src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java

+2-7
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,12 @@ private final Object _deserializeAltString(JsonParser p, DeserializationContext
167167
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
168168
return null;
169169
}
170-
} else {
171-
// [databind#149]: Allow use of 'String' indexes as well
170+
} else if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
171+
// [databind#149]: Allow use of 'String' indexes as well -- unless prohibited (as per above)
172172
char c = name.charAt(0);
173173
if (c >= '0' && c <= '9') {
174174
try {
175175
int index = Integer.parseInt(name);
176-
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
177-
return ctxt.handleWeirdNumberValue(_enumClass(), index,
178-
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
179-
);
180-
}
181176
if (index >= 0 && index < _enumsByIndex.length) {
182177
return _enumsByIndex[index];
183178
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package com.fasterxml.jackson.databind.deser;
2+
3+
import java.io.IOException;
4+
5+
import com.fasterxml.jackson.annotation.*;
6+
7+
import com.fasterxml.jackson.databind.*;
8+
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
9+
10+
public class EnumDefaultReadTest extends BaseMapTest
11+
{
12+
enum SimpleEnum {
13+
ZERO,
14+
ONE;
15+
}
16+
17+
enum SimpleEnumWithDefault {
18+
@JsonEnumDefaultValue
19+
ZERO,
20+
ONE;
21+
}
22+
23+
enum CustomEnum {
24+
ZERO(0),
25+
ONE(1);
26+
27+
private final int number;
28+
29+
CustomEnum(final int number) {
30+
this.number = number;
31+
}
32+
33+
@JsonValue
34+
int getNumber() {
35+
return this.number;
36+
}
37+
}
38+
39+
enum CustomEnumWithDefault {
40+
@JsonEnumDefaultValue
41+
ZERO(0),
42+
ONE(1);
43+
44+
private final int number;
45+
46+
CustomEnumWithDefault(final int number) {
47+
this.number = number;
48+
}
49+
50+
@JsonValue
51+
int getNumber() {
52+
return this.number;
53+
}
54+
}
55+
56+
/*
57+
/**********************************************************
58+
/* Test methods
59+
/**********************************************************
60+
*/
61+
private final ObjectMapper MAPPER = new ObjectMapper();
62+
63+
public void testWithoutCustomFeatures() throws Exception
64+
{
65+
final ObjectReader r = MAPPER.reader();
66+
67+
_verifyOkDeserialization(r, "ZERO", SimpleEnum.class, SimpleEnum.ZERO);
68+
_verifyOkDeserialization(r, "ONE", SimpleEnum.class, SimpleEnum.ONE);
69+
_verifyOkDeserialization(r, "0", SimpleEnum.class, SimpleEnum.ZERO);
70+
_verifyOkDeserialization(r, "1", SimpleEnum.class, SimpleEnum.ONE);
71+
_verifyFailingDeserialization(r, "TWO", SimpleEnum.class);
72+
_verifyFailingDeserialization(r, "2", SimpleEnum.class);
73+
74+
_verifyOkDeserialization(r, "ZERO", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
75+
_verifyOkDeserialization(r, "ONE", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ONE);
76+
_verifyFailingDeserialization(r, "TWO", SimpleEnumWithDefault.class);
77+
_verifyOkDeserialization(r, "0", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
78+
_verifyOkDeserialization(r, "1", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ONE);
79+
_verifyFailingDeserialization(r, "2", SimpleEnumWithDefault.class);
80+
81+
_verifyFailingDeserialization(r, "ZERO", CustomEnum.class);
82+
_verifyFailingDeserialization(r, "ONE", CustomEnum.class);
83+
_verifyFailingDeserialization(r, "TWO", CustomEnum.class);
84+
_verifyOkDeserialization(r, "0", CustomEnum.class, CustomEnum.ZERO);
85+
_verifyOkDeserialization(r, "1", CustomEnum.class, CustomEnum.ONE);
86+
_verifyFailingDeserialization(r, "2", CustomEnum.class);
87+
88+
_verifyFailingDeserialization(r, "ZERO", CustomEnumWithDefault.class);
89+
_verifyFailingDeserialization(r, "ONE", CustomEnumWithDefault.class);
90+
_verifyFailingDeserialization(r, "TWO", CustomEnumWithDefault.class);
91+
_verifyOkDeserialization(r, "0", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
92+
_verifyOkDeserialization(r, "1", CustomEnumWithDefault.class, CustomEnumWithDefault.ONE);
93+
_verifyFailingDeserialization(r, "2", CustomEnumWithDefault.class);
94+
}
95+
96+
public void testWithFailOnNumbers() throws Exception
97+
{
98+
ObjectReader r = MAPPER.reader()
99+
.with(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
100+
101+
_verifyOkDeserialization(r, "ZERO", SimpleEnum.class, SimpleEnum.ZERO);
102+
_verifyOkDeserialization(r, "ONE", SimpleEnum.class, SimpleEnum.ONE);
103+
_verifyFailingDeserialization(r, "TWO", SimpleEnum.class);
104+
_verifyFailingDeserialization(r, "0", SimpleEnum.class);
105+
_verifyFailingDeserialization(r, "1", SimpleEnum.class);
106+
_verifyFailingDeserialization(r, "2", SimpleEnum.class);
107+
108+
_verifyOkDeserialization(r, "ZERO", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
109+
_verifyOkDeserialization(r, "ONE", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ONE);
110+
_verifyFailingDeserialization(r, "TWO", SimpleEnumWithDefault.class);
111+
_verifyFailingDeserialization(r, "0", SimpleEnumWithDefault.class);
112+
_verifyFailingDeserialization(r, "1", SimpleEnumWithDefault.class);
113+
_verifyFailingDeserialization(r, "2", SimpleEnumWithDefault.class);
114+
115+
_verifyFailingDeserialization(r, "ZERO", CustomEnum.class);
116+
_verifyFailingDeserialization(r, "ONE", CustomEnum.class);
117+
_verifyFailingDeserialization(r, "TWO", CustomEnum.class);
118+
_verifyOkDeserialization(r, "0", CustomEnum.class, CustomEnum.ZERO);
119+
_verifyOkDeserialization(r, "1", CustomEnum.class, CustomEnum.ONE);
120+
_verifyFailingDeserialization(r, "2", CustomEnum.class);
121+
122+
_verifyFailingDeserialization(r, "ZERO", CustomEnumWithDefault.class);
123+
_verifyFailingDeserialization(r, "ONE", CustomEnumWithDefault.class);
124+
_verifyFailingDeserialization(r, "TWO", CustomEnumWithDefault.class);
125+
_verifyOkDeserialization(r, "0", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
126+
_verifyOkDeserialization(r, "1", CustomEnumWithDefault.class, CustomEnumWithDefault.ONE);
127+
_verifyFailingDeserialization(r, "2", CustomEnumWithDefault.class);
128+
}
129+
130+
public void testWithReadUnknownAsDefault() throws Exception
131+
{
132+
ObjectReader r = MAPPER.reader()
133+
.with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
134+
135+
_verifyOkDeserialization(r, "ZERO", SimpleEnum.class, SimpleEnum.ZERO);
136+
_verifyOkDeserialization(r, "ONE", SimpleEnum.class, SimpleEnum.ONE);
137+
_verifyFailingDeserialization(r, "TWO", SimpleEnum.class);
138+
_verifyOkDeserialization(r, "0", SimpleEnum.class, SimpleEnum.ZERO);
139+
_verifyOkDeserialization(r, "1", SimpleEnum.class, SimpleEnum.ONE);
140+
_verifyFailingDeserialization(r, "2", SimpleEnum.class);
141+
142+
_verifyOkDeserialization(r, "ZERO", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
143+
_verifyOkDeserialization(r, "ONE", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ONE);
144+
_verifyOkDeserialization(r, "TWO", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
145+
_verifyOkDeserialization(r, "0", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
146+
_verifyOkDeserialization(r, "1", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ONE);
147+
_verifyOkDeserialization(r, "2", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
148+
149+
_verifyFailingDeserialization(r, "ZERO", CustomEnum.class);
150+
_verifyFailingDeserialization(r, "ONE", CustomEnum.class);
151+
_verifyFailingDeserialization(r, "TWO", CustomEnum.class);
152+
_verifyOkDeserialization(r, "0", CustomEnum.class, CustomEnum.ZERO);
153+
_verifyOkDeserialization(r, "1", CustomEnum.class, CustomEnum.ONE);
154+
_verifyFailingDeserialization(r, "2", CustomEnum.class);
155+
156+
_verifyOkDeserialization(r, "ZERO", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
157+
_verifyOkDeserialization(r, "ONE", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
158+
_verifyOkDeserialization(r, "TWO", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
159+
_verifyOkDeserialization(r, "0", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
160+
_verifyOkDeserialization(r, "1", CustomEnumWithDefault.class, CustomEnumWithDefault.ONE);
161+
_verifyOkDeserialization(r, "2", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
162+
}
163+
164+
public void testWithFailOnNumbersAndReadUnknownAsDefault()
165+
throws Exception
166+
{
167+
ObjectReader r = MAPPER.reader()
168+
.with(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
169+
.with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
170+
171+
_verifyOkDeserialization(r, "ZERO", SimpleEnum.class, SimpleEnum.ZERO);
172+
_verifyOkDeserialization(r, "ONE", SimpleEnum.class, SimpleEnum.ONE);
173+
174+
_verifyFailingDeserialization(r, "TWO", SimpleEnum.class);
175+
_verifyFailingDeserialization(r, "0", SimpleEnum.class);
176+
_verifyFailingDeserialization(r, "1", SimpleEnum.class);
177+
_verifyFailingDeserialization(r, "2", SimpleEnum.class);
178+
179+
_verifyOkDeserialization(r, "ZERO", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
180+
_verifyOkDeserialization(r, "ONE", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ONE);
181+
_verifyOkDeserialization(r, "TWO", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
182+
183+
_verifyFailingDeserialization(r, "ZERO", CustomEnum.class);
184+
_verifyFailingDeserialization(r, "ONE", CustomEnum.class);
185+
_verifyFailingDeserialization(r, "TWO", CustomEnum.class);
186+
187+
_verifyOkDeserialization(r, "0", CustomEnum.class, CustomEnum.ZERO);
188+
_verifyOkDeserialization(r, "1", CustomEnum.class, CustomEnum.ONE);
189+
190+
_verifyFailingDeserialization(r, "2", CustomEnum.class);
191+
192+
_verifyOkDeserialization(r, "ZERO", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
193+
_verifyOkDeserialization(r, "ONE", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
194+
_verifyOkDeserialization(r, "TWO", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
195+
_verifyOkDeserialization(r, "0", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
196+
_verifyOkDeserialization(r, "1", CustomEnumWithDefault.class, CustomEnumWithDefault.ONE);
197+
198+
//
199+
// The three tests below fail; Jackson throws an exception on the basis that
200+
// "FAIL_ON_NUMBERS_FOR_ENUMS" is enabled. I claim the default value should be returned instead.
201+
_verifyOkDeserialization(r, "0", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
202+
_verifyOkDeserialization(r, "1", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
203+
_verifyOkDeserialization(r, "2", SimpleEnumWithDefault.class, SimpleEnumWithDefault.ZERO);
204+
205+
// Fails. Jackson throws an exception on the basis that "FAIL_ON_NUMBERS_FOR_ENUMS"
206+
// is enabled, but the default value should have been returned instead.
207+
_verifyOkDeserialization(r, "2", CustomEnumWithDefault.class, CustomEnumWithDefault.ZERO);
208+
}
209+
210+
private <T> void _verifyOkDeserialization(ObjectReader reader, String fromValue,
211+
Class<T> toValueType, T expValue)
212+
throws IOException
213+
{
214+
assertEquals(expValue, reader.forType(toValueType).readValue(quote(fromValue)));
215+
}
216+
217+
private <T> void _verifyFailingDeserialization(final ObjectReader reader,
218+
final String fromValue, final Class<T> toValueType)
219+
throws IOException
220+
{
221+
try {
222+
reader.forType(toValueType).readValue(quote(fromValue));
223+
fail("Deserialization should have failed");
224+
} catch (InvalidFormatException e) {
225+
verifyException(e, "Can not deserialize value of type");
226+
/* Expected. */
227+
}
228+
}
229+
}

src/test/java/com/fasterxml/jackson/databind/deser/EnumDeserializationTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ public void testNumbersToEnums() throws Exception
289289
fail("Expected an error");
290290
} catch (JsonMappingException e) {
291291
verifyException(e, "Can not deserialize");
292-
verifyException(e, "not allowed to deserialize Enum value out of number: disable");
292+
// 26-Jan-2017, tatu: as per [databind#1505], should fail bit differently
293+
verifyException(e, "value not one of declared Enum");
293294
}
294295
}
295296

0 commit comments

Comments
 (0)