Skip to content

Commit 493ba8d

Browse files
committed
Fixed #1161
1 parent 4a97f18 commit 493ba8d

File tree

6 files changed

+118
-32
lines changed

6 files changed

+118
-32
lines changed

release-notes/VERSION

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ Project: jackson-databind
1111
(reported by Xavi T)
1212
#1154: @JsonFormat.pattern on dates is now ignored if shape is not explicitely provided
1313
(reported by Yoann R)
14+
#1161: `DeserializationFeature.READ_ENUMS_USING_TO_STRING` not dynamically
15+
changeable with 2.7
16+
(reported by asa-git@github)
1417
- Minor fixes to `AnnotationIntrospector.findEnumValues()` to correct problems with
1518
merging of explicit enum value names.
1619

src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,8 @@ public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
12521252
}
12531253
// Need to consider @JsonValue if one found
12541254
if (deser == null) {
1255-
deser = new EnumDeserializer(constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod()));
1255+
deser = new EnumDeserializer(constructEnumResolver(enumClass,
1256+
config, beanDesc.findJsonValueMethod()));
12561257
}
12571258
}
12581259

@@ -1436,7 +1437,6 @@ private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt,
14361437
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, valueDesForKey);
14371438
}
14381439
}
1439-
14401440
EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
14411441
// May have @JsonCreator for static factory method:
14421442
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
@@ -1919,10 +1919,8 @@ protected EnumResolver constructEnumResolver(Class<?> enumClass,
19191919
}
19201920
return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor);
19211921
}
1922-
// May need to use Enum.toString()
1923-
if (config.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)) {
1924-
return EnumResolver.constructUnsafeUsingToString(enumClass);
1925-
}
1922+
// 14-Mar-2016, tatu: We used to check `DeserializationFeature.READ_ENUMS_USING_TO_STRING`
1923+
// here, but that won't do: it must be dynamically changeable...
19261924
return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
19271925
}
19281926

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

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,26 @@ public class EnumDeserializer
2727
/**
2828
* @since 2.6
2929
*/
30-
protected final CompactStringObjectMap _enumLookup;
30+
protected Object[] _enumsByIndex;
3131

3232
/**
33-
* @since 2.6
33+
* @since 2.7.3
3434
*/
35-
protected Object[] _enumsByIndex;
35+
protected final CompactStringObjectMap _lookupByName;
3636

37-
public EnumDeserializer(EnumResolver res)
37+
/**
38+
* Alternatively, we may need a different lookup object if "use toString"
39+
* is defined.
40+
*
41+
* @since 2.7.3
42+
*/
43+
protected CompactStringObjectMap _lookupByToString;
44+
45+
public EnumDeserializer(EnumResolver byNameResolver)
3846
{
39-
super(res.getEnumClass());
40-
_enumLookup = res.constructLookup();
41-
_enumsByIndex = res.getRawEnums();
47+
super(byNameResolver.getEnumClass());
48+
_lookupByName = byNameResolver.constructLookup();
49+
_enumsByIndex = byNameResolver.getRawEnums();
4250
}
4351

4452
/**
@@ -80,10 +88,12 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
8088

8189
// Usually should just get string value:
8290
if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
83-
String name = p.getText();
84-
Object result = _enumLookup.find(name);
91+
CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
92+
? _getToStringLookup() : _lookupByName;
93+
final String name = p.getText();
94+
Object result = lookup.find(name);
8595
if (result == null) {
86-
return _deserializeAltString(p, ctxt, name);
96+
return _deserializeAltString(p, ctxt, lookup, name);
8797
}
8898
return result;
8999
}
@@ -106,8 +116,14 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
106116
return _deserializeOther(p, ctxt);
107117
}
108118

119+
/*
120+
/**********************************************************
121+
/* Internal helper methods
122+
/**********************************************************
123+
*/
124+
109125
private final Object _deserializeAltString(JsonParser p, DeserializationContext ctxt,
110-
String name) throws IOException
126+
CompactStringObjectMap lookup, String name) throws IOException
111127
{
112128
name = name.trim();
113129
if (name.length() == 0) {
@@ -133,7 +149,7 @@ private final Object _deserializeAltString(JsonParser p, DeserializationContext
133149
}
134150
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
135151
throw ctxt.weirdStringException(name, _enumClass(),
136-
"value not one of declared Enum instance names: "+_enumLookup.keys());
152+
"value not one of declared Enum instance names: "+lookup.keys());
137153
}
138154
return null;
139155
}
@@ -169,6 +185,21 @@ protected Class<?> _enumClass() {
169185
return handledType();
170186
}
171187

188+
protected CompactStringObjectMap _getToStringLookup()
189+
{
190+
CompactStringObjectMap lookup = _lookupByToString;
191+
// note: exact locking not needed; all we care for here is to try to
192+
// reduce contention for the initial resolution
193+
if (lookup == null) {
194+
synchronized (this) {
195+
lookup = EnumResolver.constructUnsafeUsingToString(_enumClass())
196+
.constructLookup();
197+
}
198+
_lookupByToString = lookup;
199+
}
200+
return lookup;
201+
}
202+
172203
/*
173204
/**********************************************************
174205
/* Additional helper classes

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

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public static StdKeyDeserializer forType(Class<?> raw)
117117

118118
@Override
119119
public Object deserializeKey(String key, DeserializationContext ctxt)
120-
throws IOException, JsonProcessingException
120+
throws IOException
121121
{
122122
if (key == null) { // is this even legal call?
123123
return null;
@@ -318,13 +318,21 @@ final static class EnumKD extends StdKeyDeserializer
318318
{
319319
private static final long serialVersionUID = 1L;
320320

321-
protected final EnumResolver _resolver;
321+
protected final EnumResolver _byNameResolver;
322322

323323
protected final AnnotatedMethod _factory;
324324

325+
/**
326+
* Lazily constructed alternative in case there is need to
327+
* use 'toString()' method as the source.
328+
*
329+
* @since 2.7.3
330+
*/
331+
protected EnumResolver _byToStringResolver;
332+
325333
protected EnumKD(EnumResolver er, AnnotatedMethod factory) {
326334
super(-1, er.getEnumClass());
327-
_resolver = er;
335+
_byNameResolver = er;
328336
_factory = factory;
329337
}
330338

@@ -338,12 +346,26 @@ public Object _parse(String key, DeserializationContext ctxt) throws JsonMapping
338346
ClassUtil.unwrapAndThrowAsIAE(e);
339347
}
340348
}
341-
Enum<?> e = _resolver.findEnum(key);
349+
EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
350+
? _getToStringResolver() : _byNameResolver;
351+
Enum<?> e = res.findEnum(key);
342352
if ((e == null) && !ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
343-
throw ctxt.weirdKeyException(_keyClass, key, "not one of values for Enum class");
353+
throw ctxt.weirdKeyException(_keyClass, key, "not one of values excepted for Enum class: "
354+
+res.getEnumIds());
344355
}
345356
return e;
346357
}
358+
359+
private EnumResolver _getToStringResolver()
360+
{
361+
EnumResolver res = _byToStringResolver;
362+
if (res == null) {
363+
synchronized (this) {
364+
res = EnumResolver.constructUnsafeUsingToString(_byNameResolver.getEnumClass());
365+
}
366+
}
367+
return res;
368+
}
347369
}
348370

349371
/**

src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.fasterxml.jackson.databind.util;
22

3-
import com.fasterxml.jackson.databind.AnnotationIntrospector;
4-
53
import java.lang.reflect.Method;
64
import java.util.*;
75

8-
/**
6+
import com.fasterxml.jackson.databind.AnnotationIntrospector;
7+
8+
/**
99
* Helper class used to resolve String values (either JSON Object field
1010
* names or regular String values) into Java Enum instances.
1111
*/
@@ -62,7 +62,7 @@ public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls)
6262
map.put(e.toString(), e);
6363
}
6464
return new EnumResolver(enumCls, enumValues, map);
65-
}
65+
}
6666

6767
public static EnumResolver constructUsingMethod(Class<Enum<?>> enumCls,
6868
Method accessor)
@@ -146,6 +146,13 @@ public List<Enum<?>> getEnums() {
146146
}
147147
return enums;
148148
}
149+
150+
/**
151+
* @since 2.7.3
152+
*/
153+
public Collection<String> getEnumIds() {
154+
return _enumsById.keySet();
155+
}
149156

150157
public Class<Enum<?>> getEnumClass() { return _enumClass; }
151158

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

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
import java.io.IOException;
44
import java.math.BigDecimal;
5-
import java.util.Collection;
6-
import java.util.EnumMap;
7-
import java.util.EnumSet;
8-
import java.util.Map;
5+
import java.util.*;
96
import java.util.concurrent.TimeUnit;
107

118
import com.fasterxml.jackson.annotation.*;
@@ -167,6 +164,16 @@ public String toString() {
167164
;
168165
}
169166

167+
// [databind#1161]
168+
enum Enum1161 {
169+
A, B, C;
170+
171+
@Override
172+
public String toString() {
173+
return name().toLowerCase();
174+
};
175+
}
176+
170177
/*
171178
/**********************************************************
172179
/* Tests
@@ -481,4 +488,22 @@ public void testEnumWithJsonPropertyRename() throws Exception
481488
assertSame(EnumWithPropertyAnno.B, result[0]);
482489
assertSame(EnumWithPropertyAnno.A, result[1]);
483490
}
484-
}
491+
492+
// [databind#1161], unable to switch READ_ENUMS_USING_TO_STRING
493+
public void testDeserWithToString1161() throws Exception
494+
{
495+
Enum1161 result = MAPPER.readerFor(Enum1161.class)
496+
.readValue(quote("A"));
497+
assertSame(Enum1161.A, result);
498+
499+
result = MAPPER.readerFor(Enum1161.class)
500+
.with(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
501+
.readValue(quote("a"));
502+
assertSame(Enum1161.A, result);
503+
504+
// and once again, going back to defaults
505+
result = MAPPER.readerFor(Enum1161.class)
506+
.without(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
507+
.readValue(quote("A"));
508+
assertSame(Enum1161.A, result);
509+
}}

0 commit comments

Comments
 (0)