Skip to content

Commit

Permalink
Feature #2536 : "READ_ENUM_KEYS_USING_INDEX" to match "WRITE_ENUM_KEY…
Browse files Browse the repository at this point in the history
…S_USING_INDEX" (#3764)
  • Loading branch information
JooHyukKim authored Feb 6, 2023
1 parent 660eede commit d75496b
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.fasterxml.jackson.databind.cfg;

import com.fasterxml.jackson.databind.SerializationFeature;

/**
* New Datatype-specific configuration options related to handling of
* {@link java.lang.Enum} types.
Expand All @@ -8,7 +10,21 @@
*/
public enum EnumFeature implements DatatypeFeature
{
BOGUS_FEATURE(false);
BOGUS_FEATURE(false),

/**
* Feature that determines standard deserialization mechanism used for
* Enum values: if enabled, Enums are assumed to have been serialized using
* index of <code>Enum</code>;
*<p>
* Note: this feature should be symmetric to
* as {@link SerializationFeature#WRITE_ENUM_KEYS_USING_INDEX}.
*<p>
* Feature is disabled by default.
*
* @since 2.15
*/
READ_ENUM_KEYS_USING_INDEX(false);

private final static int FEATURE_INDEX = DatatypeFeatures.FEATURE_INDEX_ENUM;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.cfg.EnumFeature;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.EnumResolver;
Expand Down Expand Up @@ -370,6 +371,14 @@ final static class EnumKD extends StdKeyDeserializer
*/
protected volatile EnumResolver _byToStringResolver;

/**
* Lazily constructed alternative in case there is need to
* parse using enum index method as the source.
*
* @since 2.15
*/
protected volatile EnumResolver _byIndexResolver;

protected final Enum<?> _enumDefaultValue;

protected EnumKD(EnumResolver er, AnnotatedMethod factory) {
Expand All @@ -392,6 +401,11 @@ public Object _parse(String key, DeserializationContext ctxt) throws IOException
EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
? _getToStringResolver(ctxt) : _byNameResolver;
Enum<?> e = res.findEnum(key);
// If enum is found, no need to try deser using index
if (e == null && ctxt.isEnabled(EnumFeature.READ_ENUM_KEYS_USING_INDEX)) {
res = _getIndexResolver(ctxt);
e = res.findEnum(key);
}
if (e == null) {
if ((_enumDefaultValue != null)
&& ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
Expand Down Expand Up @@ -420,6 +434,21 @@ private EnumResolver _getToStringResolver(DeserializationContext ctxt)
}
return res;
}

private EnumResolver _getIndexResolver(DeserializationContext ctxt) {
EnumResolver res = _byIndexResolver;
if (res == null) {
synchronized (this) {
res = _byIndexResolver;
if (res == null) {
res = EnumResolver.constructUsingIndex(ctxt.getConfig(),
_byNameResolver.getEnumClass());
_byIndexResolver = res;
}
}
}
return res;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,31 @@ public static EnumResolver constructUsingToString(DeserializationConfig config,
}

/**
* Factory method for constructing resolver that maps from index of Enum.values() into
* Enum value
*
* @since 2.15
*/
public static EnumResolver constructUsingIndex(DeserializationConfig config, Class<Enum<?>> enumCls) {
return _constructUsingIndex(enumCls, config.getAnnotationIntrospector(),
config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS));
}

private static EnumResolver _constructUsingIndex(Class<Enum<?>> enumCls0, AnnotationIntrospector ai, boolean isIgnoreCase) {
final Class<Enum<?>> enumCls = _enumClass(enumCls0);
final Enum<?>[] enumConstants = _enumConstants(enumCls0);
HashMap<String, Enum<?>> map = new HashMap<>();

// from last to first, so that in case of duplicate values, first wins
for (int i = enumConstants.length; --i >= 0; ) {
Enum<?> enumValue = enumConstants[i];
map.put(String.valueOf(i), enumValue);
}
return new EnumResolver(enumCls, enumConstants, map,
_enumDefault(ai, enumCls), isIgnoreCase, false);
}

/**
* @since 2.12
*/
protected static EnumResolver _constructUsingToString(Class<?> enumCls0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.cfg.EnumFeature;
import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
Expand Down Expand Up @@ -654,4 +655,41 @@ public void testIssue3006() throws Exception
assertEquals(Operation3006.THREE, MAPPER.readValue("3", Operation3006.class));
assertEquals(Operation3006.THREE, MAPPER.readValue(q("3"), Operation3006.class));
}

public void testEnumFeature_EnumIndexAsKey() throws Exception {
ObjectReader reader = MAPPER.reader()
.with(EnumFeature.READ_ENUM_KEYS_USING_INDEX);

ClassWithEnumMapKey result = reader.readValue("{\"map\": {\"0\":\"I AM FOR REAL\"}}", ClassWithEnumMapKey.class);

assertEquals(result.map.get(TestEnum.JACKSON), "I AM FOR REAL");
}

public void testEnumFeature_symmetric_to_writing() throws Exception {
ClassWithEnumMapKey obj = new ClassWithEnumMapKey();
Map<TestEnum, String> objMap = new HashMap<>();
objMap.put(TestEnum.JACKSON, "I AM FOR REAL");
obj.map = objMap;

String deserObj = MAPPER.writer()
.with(SerializationFeature.WRITE_ENUM_KEYS_USING_INDEX)
.writeValueAsString(obj);

ClassWithEnumMapKey result = MAPPER.reader()
.with(EnumFeature.READ_ENUM_KEYS_USING_INDEX)
.readValue(deserObj, ClassWithEnumMapKey.class);

assertNotSame(obj, result);
assertNotSame(obj.map, result.map);
assertEquals(result.map.get(TestEnum.JACKSON), "I AM FOR REAL");
}


public void testEnumFeature_READ_ENUM_KEYS_USING_INDEX_isDisabledByDefault() {
ObjectReader READER = MAPPER.reader();
assertFalse(READER.isEnabled(EnumFeature.READ_ENUM_KEYS_USING_INDEX));
assertFalse(READER.without(EnumFeature.READ_ENUM_KEYS_USING_INDEX)
.isEnabled(EnumFeature.READ_ENUM_KEYS_USING_INDEX));
}

}

0 comments on commit d75496b

Please sign in to comment.