Skip to content

Commit f875dbc

Browse files
committed
Fix #3006
1 parent a6ce028 commit f875dbc

File tree

3 files changed

+65
-20
lines changed

3 files changed

+65
-20
lines changed

release-notes/VERSION-2.x

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ Project: jackson-databind
44
=== Releases ===
55
------------------------------------------------------------------------
66

7+
2.13.1 (not yet released)
8+
9+
#3006: Argument type mismatch for `enum` with `@JsonCreator` that takes String,
10+
gets JSON Number
11+
(reported by GrozaAnton@github)
12+
713
2.13.0 (30-Sep-2021)
814

915
#1850: `@JsonValue` with integer for enum does not deserialize correctly

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

+23-18
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ class FactoryBasedEnumDeserializer
2929
{
3030
private static final long serialVersionUID = 1;
3131

32-
// Marker type; null if String expected; otherwise numeric wrapper
32+
// Marker type; null if String expected; otherwise usually numeric wrapper
3333
protected final JavaType _inputType;
34-
protected final boolean _hasArgs;
3534
protected final AnnotatedMethod _factory;
3635
protected final JsonDeserializer<?> _deser;
3736
protected final ValueInstantiator _valueInstantiator;
3837
protected final SettableBeanProperty[] _creatorProps;
3938

39+
protected final boolean _hasArgs;
40+
4041
/**
4142
* Lazily instantiated property-based creator.
4243
*
@@ -51,7 +52,8 @@ public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType pa
5152
_factory = f;
5253
_hasArgs = true;
5354
// We'll skip case of `String`, as well as no type (zero-args):
54-
_inputType = paramType.hasRawClass(String.class) ? null : paramType;
55+
_inputType = (paramType.hasRawClass(String.class) || paramType.hasRawClass(CharSequence.class))
56+
? null : paramType;
5557
_deser = null;
5658
_valueInstantiator = valueInstantiator;
5759
_creatorProps = creatorProps;
@@ -88,6 +90,9 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
8890
BeanProperty property)
8991
throws JsonMappingException
9092
{
93+
// So: no need to fetch if we had it; or if target is `String`(-like); or
94+
// if we have properties-based Creator (for which we probably SHOULD do
95+
// different contextualization?)
9196
if ((_deser == null) && (_inputType != null) && (_creatorProps == null)) {
9297
return new FactoryBasedEnumDeserializer(this,
9398
ctxt.findContextualValueDeserializer(_inputType, property));
@@ -116,11 +121,14 @@ public LogicalType logicalType() {
116121
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
117122
{
118123
Object value;
124+
125+
// First: the case of having deserializer for non-String input for delegating
126+
// Creator method
119127
if (_deser != null) {
120128
value = _deser.deserialize(p, ctxt);
121-
} else if (_hasArgs) {
122-
JsonToken curr = p.currentToken();
123129

130+
// Second: property- and delegating-creators
131+
} else if (_hasArgs) {
124132
// 30-Mar-2020, tatu: For properties-based one, MUST get JSON Object (before
125133
// 2.11, was just assuming match)
126134
if (_creatorProps != null) {
@@ -138,15 +146,9 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
138146
return deserializeEnumUsingPropertyBased(p, ctxt, _propCreator);
139147
}
140148

141-
// 30-Mar-2020, tatu: Single-arg delegating creators may go through
142-
// here; although not 100% sure why they do not take the first branch
143-
if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
144-
value = p.getText();
145-
} else if (curr == JsonToken.VALUE_NUMBER_INT) {
146-
value = p.getNumberValue();
147-
} else {
148-
value = p.getValueAsString();
149-
}
149+
// 12-Oct-2021, tatu: We really should only get here if and when String
150+
// value is expected; otherwise Deserializer should have been used earlier
151+
value = p.getValueAsString();
150152
} else { // zero-args; just skip whatever value there may be
151153
p.skipChildren();
152154
try {
@@ -160,10 +162,13 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
160162
return _factory.callOnWith(_valueClass, value);
161163
} catch (Exception e) {
162164
Throwable t = ClassUtil.throwRootCauseIfIOE(e);
163-
// [databind#1642]:
164-
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL) &&
165-
t instanceof IllegalArgumentException) {
166-
return null;
165+
if (t instanceof IllegalArgumentException) {
166+
// [databind#1642]:
167+
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
168+
return null;
169+
}
170+
// 12-Oct-2021, tatu: Should probably try to provide better exception since
171+
// we likely hit argument incompatibility... Or can this happen?
167172
}
168173
return ctxt.handleInstantiationProblem(_valueClass, value, t);
169174
}

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

+36-2
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ static enum StrictEnumCreator {
134134
}
135135
}
136136

137-
// //
138-
139137
public enum AnEnum {
140138
ZERO,
141139
ONE
@@ -217,6 +215,33 @@ public String toString() {
217215
}
218216
}
219217

218+
// [databind#3006]
219+
enum Operation3006 {
220+
ONE(1L), TWO(2L), THREE(3L);
221+
222+
private static final Map<Long, Operation3006> mapping = new HashMap<>();
223+
static {
224+
for (Operation3006 operation : Operation3006.values()) {
225+
mapping.put(operation.id, operation);
226+
}
227+
}
228+
229+
final long id;
230+
231+
Operation3006(final long id) {
232+
this.id = id;
233+
}
234+
235+
@JsonCreator
236+
public static Operation3006 forValue(final String idStr) {
237+
Operation3006 candidate = mapping.get(Long.parseLong(idStr));
238+
if (candidate == null) {
239+
throw new IllegalArgumentException("Unable to find: " + idStr);
240+
}
241+
return candidate;
242+
}
243+
}
244+
220245
/*
221246
/**********************************************************
222247
/* Test methods
@@ -618,4 +643,13 @@ public void testAllowCaseInsensitiveEnumValues() throws Exception {
618643
assertEquals(1, result.map.size());
619644
assertEquals("val", result.map.get(TestEnum.JACKSON));
620645
}
646+
647+
// [databind#3006]
648+
public void testIssue3006() throws Exception
649+
{
650+
assertEquals(Operation3006.ONE, MAPPER.readValue("1", Operation3006.class));
651+
assertEquals(Operation3006.ONE, MAPPER.readValue(q("1"), Operation3006.class));
652+
assertEquals(Operation3006.THREE, MAPPER.readValue("3", Operation3006.class));
653+
assertEquals(Operation3006.THREE, MAPPER.readValue(q("3"), Operation3006.class));
654+
}
621655
}

0 commit comments

Comments
 (0)