aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release-notes/CREDITS-2.x4
-rw-r--r--release-notes/VERSION-2.x2
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java13
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java7
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java24
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java49
-rw-r--r--src/test/java/com/fasterxml/jackson/databind/deser/jdk/EnumAltIdTest.java87
-rw-r--r--src/test/java/com/fasterxml/jackson/databind/util/EnumValuesTest.java3
8 files changed, 173 insertions, 16 deletions
diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x
index 656cdc3a9..59b3a393f 100644
--- a/release-notes/CREDITS-2.x
+++ b/release-notes/CREDITS-2.x
@@ -1015,6 +1015,10 @@ Antonio Petrelli (apetrelli@github)
* Reported #2049: TreeTraversingParser and UTF8StreamJsonParser create contexts differently
(2.11.0)
+Robert Diebels (RobertDiebels@github)
+ * Contributed #2352: Support use of `@JsonAlias` for enum values
+ (2.11.0)
+
Joseph Koshakow (jkosh44@github)
* Contributed fix for #2515: `ObjectMapper.registerSubtypes(NamedType...)` doesn't allow registering
the same POJO for two different type ids
diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x
index c75d83f10..0d9ca2bdb 100644
--- a/release-notes/VERSION-2.x
+++ b/release-notes/VERSION-2.x
@@ -8,6 +8,8 @@ Project: jackson-databind
#2049: TreeTraversingParser and UTF8StreamJsonParser create contexts differently
(reported by Antonio P)
+#2352: Support use of `@JsonAlias` for enum values
+ (contributed by Robert D)
#2480: Fix `JavaType.isEnumType()` to support sub-classes
#2487: BeanDeserializerBuilder Protected Factory Method for Extension
(contributed by Ville K)
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));