diff options
Diffstat (limited to 'src')
6 files changed, 167 insertions, 16 deletions
diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java index 51a30bd48..9b701ec3d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java @@ -934,6 +934,19 @@ public abstract class AnnotationIntrospector } /** + * Method that is related to {@link #findEnumValues} but is called to check if + * there are alternative names (aliased) that can be accepted for entries, in + * addition to primary names introspected earlier. + * If so, these aliases should be returned in {@code aliases} {@link List} passed + * as argument (and initialized for proper size by caller). + * + * @since 2.11 + */ + public void findEnumAliases(Class<?> enumType, Enum<?>[] enumValues, String[][] aliases) { + ; + } + + /** * Finds the Enum value that should be considered the default value, if possible. * * @param enumCls The Enum class to scan for the default value. diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java index aa288b9e1..60efe6e52 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java @@ -617,6 +617,13 @@ public class AnnotationIntrospectorPair } @Override + public void findEnumAliases(Class<?> enumType, Enum<?>[] enumValues, String[][] aliases) { + // reverse order to give _primary higher precedence + _secondary.findEnumAliases(enumType, enumValues, aliases); + _primary.findEnumAliases(enumType, enumValues, aliases); + } + + @Override public Enum<?> findDefaultEnumValue(Class<Enum<?>> enumCls) { Enum<?> en = _primary.findDefaultEnumValue(enumCls); return (en == null) ? _secondary.findDefaultEnumValue(enumCls) : en; diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java index 55c11d187..cbe026598 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java @@ -234,6 +234,30 @@ public class JacksonAnnotationIntrospector return names; } + @Override // since 2.11 + public void findEnumAliases(Class<?> enumType, Enum<?>[] enumValues, String[][] aliasList) + { + // Main complication: discrepancy between Field that represent enum value, + // Enum abstraction; joint by name but not reference + for (Field f : ClassUtil.getDeclaredFields(enumType)) { + if (f.isEnumConstant()) { + JsonAlias aliasAnnotation = f.getAnnotation(JsonAlias.class); + if (aliasAnnotation != null) { + String[] aliases = aliasAnnotation.value(); + if (aliases.length != 0) { + final String name = f.getName(); + // Find matching enum (could create Ma + for (int i = 0, end = enumValues.length; i < end; ++i) { + if (name.equals(enumValues[i].name())) { + aliasList[i] = aliases; + } + } + } + } + } + } + } + /** * Finds the Enum value that should be considered the default value, if possible. * <p> diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java index f95ea55f4..5effa10a0 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java @@ -21,7 +21,8 @@ public class EnumResolver implements java.io.Serializable protected final Enum<?> _defaultValue; - protected EnumResolver(Class<Enum<?>> enumClass, Enum<?>[] enums, HashMap<String, Enum<?>> map, Enum<?> defaultValue) + protected EnumResolver(Class<Enum<?>> enumClass, Enum<?>[] enums, + HashMap<String, Enum<?>> map, Enum<?> defaultValue) { _enumClass = enumClass; _enums = enums; @@ -40,18 +41,28 @@ public class EnumResolver implements java.io.Serializable throw new IllegalArgumentException("No enum constants for class "+enumCls.getName()); } String[] names = ai.findEnumValues(enumCls, enumValues, new String[enumValues.length]); + final String[][] allAliases = new String[names.length][]; + ai.findEnumAliases(enumCls, enumValues, allAliases); HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>(); for (int i = 0, len = enumValues.length; i < len; ++i) { + final Enum<?> enumValue = enumValues[i]; String name = names[i]; if (name == null) { - name = enumValues[i].name(); + name = enumValue.name(); + } + map.put(name, enumValue); + String[] aliases = allAliases[i]; + if (aliases != null) { + for (String alias : aliases) { + // TODO: JDK 1.8, use Map.putIfAbsent() + // Avoid overriding any primary names + if (!map.containsKey(alias)) { + map.put(alias, enumValue); + } + } } - map.put(name, enumValues[i]); } - - Enum<?> defaultEnum = ai.findDefaultEnumValue(enumCls); - - return new EnumResolver(enumCls, enumValues, map, defaultEnum); + return new EnumResolver(enumCls, enumValues, map, ai.findDefaultEnumValue(enumCls)); } /** @@ -71,15 +82,27 @@ public class EnumResolver implements java.io.Serializable public static EnumResolver constructUsingToString(Class<Enum<?>> enumCls, AnnotationIntrospector ai) { - Enum<?>[] enumValues = enumCls.getEnumConstants(); + Enum<?>[] enumConstants = enumCls.getEnumConstants(); HashMap<String, Enum<?>> map = new HashMap<String, Enum<?>>(); + final String[][] allAliases = new String[enumConstants.length][]; + ai.findEnumAliases(enumCls, enumConstants, allAliases); + // from last to first, so that in case of duplicate values, first wins - for (int i = enumValues.length; --i >= 0; ) { - Enum<?> e = enumValues[i]; - map.put(e.toString(), e); + for (int i = enumConstants.length; --i >= 0; ) { + Enum<?> enumValue = enumConstants[i]; + map.put(enumValue.toString(), enumValue); + String[] aliases = allAliases[i]; + if (aliases != null) { + for (String alias : aliases) { + // TODO: JDK 1.8, use Map.putIfAbsent() + // Avoid overriding any primary names + if (!map.containsKey(alias)) { + map.put(alias, enumValue); + } + } + } } - Enum<?> defaultEnum = (ai == null) ? null : ai.findDefaultEnumValue(enumCls); - return new EnumResolver(enumCls, enumValues, map, defaultEnum); + return new EnumResolver(enumCls, enumConstants, map, ai.findDefaultEnumValue(enumCls)); } /** diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java index 1b1e7ecf4..0aa1d0af6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java @@ -3,6 +3,8 @@ package com.fasterxml.jackson.databind.deser.jdk; import java.io.IOException; import java.util.EnumSet; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.BaseMapTest; @@ -33,7 +35,39 @@ public class EnumAltIdTest extends BaseMapTest @JsonFormat(without={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES }) public TestEnum value; } - + + + // for [databind#2352]: Support aliases on enum values + enum MyEnum2352_1 { + A, + @JsonAlias({"singleAlias"}) + B, + @JsonAlias({"multipleAliases1", "multipleAliases2"}) + C + } + // for [databind#2352]: Support aliases on enum values + enum MyEnum2352_2 { + A, + @JsonAlias({"singleAlias"}) + B, + @JsonAlias({"multipleAliases1", "multipleAliases2"}) + C; + + @Override + public String toString() { + return name().toLowerCase(); + } + } + // for [databind#2352]: Support aliases on enum values + enum MyEnum2352_3 { + A, + @JsonEnumDefaultValue + @JsonAlias({"singleAlias"}) + B, + @JsonAlias({"multipleAliases1", "multipleAliases2"}) + C; + } + /* /********************************************************** /* Test methods, basic @@ -59,7 +93,7 @@ public class EnumAltIdTest extends BaseMapTest verifyException(e, "[JACKSON, OK, RULES]"); } } - + public void testFailWhenCaseSensitiveAndToStringIsUpperCase() throws IOException { ObjectReader r = READER_DEFAULT.forType(LowerCaseEnum.class) .with(DeserializationFeature.READ_ENUMS_USING_TO_STRING); @@ -130,4 +164,53 @@ public class EnumAltIdTest extends BaseMapTest verifyException(e, "[JACKSON, OK, RULES]"); } } + + /* + /********************************************************** + /* Test methods, Enum Aliases [databind#2352] + /********************************************************** + */ + + // for [databind#2352] + public void testEnumWithAlias() throws Exception { + ObjectReader reader = MAPPER.readerFor(MyEnum2352_1.class); + MyEnum2352_1 nonAliased = reader.readValue(quote("A")); + assertEquals(MyEnum2352_1.A, nonAliased); + MyEnum2352_1 singleAlias = reader.readValue(quote("singleAlias")); + assertEquals(MyEnum2352_1.B, singleAlias); + MyEnum2352_1 multipleAliases1 = reader.readValue(quote("multipleAliases1")); + assertEquals(MyEnum2352_1.C, multipleAliases1); + MyEnum2352_1 multipleAliases2 = reader.readValue(quote("multipleAliases2")); + assertEquals(MyEnum2352_1.C, multipleAliases2); + } + + // for [databind#2352] + public void testEnumWithAliasAndToStringSupported() throws Exception { + ObjectReader reader = MAPPER.readerFor(MyEnum2352_2.class) + .with(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + MyEnum2352_2 nonAliased = reader.readValue(quote("a")); + assertEquals(MyEnum2352_2.A, nonAliased); + MyEnum2352_2 singleAlias = reader.readValue(quote("singleAlias")); + assertEquals(MyEnum2352_2.B, singleAlias); + MyEnum2352_2 multipleAliases1 = reader.readValue(quote("multipleAliases1")); + assertEquals(MyEnum2352_2.C, multipleAliases1); + MyEnum2352_2 multipleAliases2 = reader.readValue(quote("multipleAliases2")); + assertEquals(MyEnum2352_2.C, multipleAliases2); + } + + // for [databind#2352] + public void testEnumWithAliasAndDefaultForUnknownValueEnabled() throws Exception { + ObjectReader reader = MAPPER.readerFor(MyEnum2352_3.class) + .with(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE); + MyEnum2352_3 nonAliased = reader.readValue(quote("A")); + assertEquals(MyEnum2352_3.A, nonAliased); + MyEnum2352_3 singleAlias = reader.readValue(quote("singleAlias")); + assertEquals(MyEnum2352_3.B, singleAlias); + MyEnum2352_3 defaulted = reader.readValue(quote("unknownValue")); + assertEquals(MyEnum2352_3.B, defaulted); + MyEnum2352_3 multipleAliases1 = reader.readValue(quote("multipleAliases1")); + assertEquals(MyEnum2352_3.C, multipleAliases1); + MyEnum2352_3 multipleAliases2 = reader.readValue(quote("multipleAliases2")); + assertEquals(MyEnum2352_3.C, multipleAliases2); + } } diff --git a/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java b/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java index ccc3ea263..d5548b7d4 100644 --- a/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java @@ -52,7 +52,8 @@ public class EnumValuesTest extends BaseMapTest public void testEnumResolver() { - EnumResolver enumRes = EnumResolver.constructUnsafeUsingToString(ABC.class, null); + EnumResolver enumRes = EnumResolver.constructUnsafeUsingToString(ABC.class, + MAPPER.getSerializationConfig().getAnnotationIntrospector()); assertEquals(ABC.B, enumRes.getEnum(1)); assertNull(enumRes.getEnum(-1)); assertNull(enumRes.getEnum(3)); |