From 4582a5de0092f6a842f425c85d1c95a17ca0f1c1 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Fri, 14 Jul 2023 15:01:51 -0500 Subject: [PATCH 1/5] feat: use JsonProperty and WRITE_ENUMS_TO_LOWERCASE despite WRITE_ENUMS_USING_TO_STRING --- .../databind/ser/std/EnumSerializer.java | 31 ++++++++++++-- .../jackson/databind/util/EnumValues.java | 42 ++++++++++++++++++- .../ser/jdk/EnumSerializationTest.java | 7 ++++ 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java index 6e8e192082..18e70d2bce 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java @@ -59,6 +59,15 @@ public class EnumSerializer */ protected final EnumValues _valuesByEnumNaming; + /** + * Map that contains pre-resolved values for Enum#toString to use for serialization, + * while respecting {@link com.fasterxml.jackson.annotation.JsonProperty} + * and {@link com.fasterxml.jackson.databind.cfg.EnumFeature#WRITE_ENUMS_TO_LOWERCASE}. + * + * @since 2.16 + */ + protected final EnumValues _valuesByToString; + /* /********************************************************** /* Construction, initialization @@ -71,6 +80,7 @@ public EnumSerializer(EnumValues v, Boolean serializeAsIndex) _values = v; _serializeAsIndex = serializeAsIndex; _valuesByEnumNaming = null; + _valuesByToString = null; } /** @@ -82,6 +92,20 @@ public EnumSerializer(EnumValues v, Boolean serializeAsIndex, EnumValues valuesB _values = v; _serializeAsIndex = serializeAsIndex; _valuesByEnumNaming = valuesByEnumNaming; + _valuesByToString = null; + } + + /** + * @since 2.16 + */ + public EnumSerializer(EnumValues v, Boolean serializeAsIndex, EnumValues valuesByEnumNaming, + EnumValues valuesByToString) + { + super(v.getEnumClass(), false); + _values = v; + _serializeAsIndex = serializeAsIndex; + _valuesByEnumNaming = valuesByEnumNaming; + _valuesByToString = valuesByToString; } /** @@ -99,9 +123,10 @@ public static EnumSerializer construct(Class enumClass, SerializationConfig c * handle toString() case dynamically (for example) */ EnumValues v = EnumValues.constructFromName(config, beanDesc.getClassInfo()); + EnumValues valuesByToString = EnumValues.constructFromToString(config, beanDesc.getClassInfo()); EnumValues valuesByEnumNaming = constructEnumNamingStrategyValues(config, (Class>) enumClass, beanDesc.getClassInfo()); Boolean serializeAsIndex = _isShapeWrittenUsingIndex(enumClass, format, true, null); - return new EnumSerializer(v, serializeAsIndex, valuesByEnumNaming); + return new EnumSerializer(v, serializeAsIndex, valuesByEnumNaming, valuesByToString); } /** @@ -154,7 +179,7 @@ public final void serialize(Enum en, JsonGenerator gen, SerializerProvider se } // [databind#749]: or via toString()? if (serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { - gen.writeString(en.toString()); + gen.writeString(_valuesByToString.serializedValueFor(en)); return; } gen.writeString(_values.serializedValueFor(en)); @@ -205,7 +230,7 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t // Use toString()? if ((serializers != null) && serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { - for (Enum e : _values.enums()) { + for (Enum e : _valuesByToString.enums()) { enums.add(e.toString()); } } else { diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java index 37a14d6e6f..91921b18e9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java @@ -39,7 +39,7 @@ private EnumValues(Class> enumClass, SerializableString[] textual) */ public static EnumValues construct(SerializationConfig config, AnnotatedClass annotatedClass) { if (config.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { - return constructFromToString(config, _enumClass(annotatedClass.getRawType())); + return constructFromToString(config, annotatedClass); } return constructFromName(config, annotatedClass); } @@ -104,6 +104,44 @@ public static EnumValues constructFromName(MapperConfig config, AnnotatedClas return construct(enumCls, textual); } + /** + * @since 2.16 + */ + public static EnumValues constructFromToString(MapperConfig config, AnnotatedClass annotatedClass) + { + // prepare data + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + final boolean useLowerCase = config.isEnabled(EnumFeature.WRITE_ENUMS_TO_LOWERCASE); + final Class enumCls0 = annotatedClass.getRawType(); + final Class> enumCls = _enumClass(enumCls0); + final Enum[] enumConstants = _enumConstants(enumCls0); + + // introspect + String[] names = new String[enumConstants.length]; + if (ai != null) { + ai.findEnumValues(config, annotatedClass, enumConstants, names); + } + + // build + SerializableString[] textual = new SerializableString[enumConstants.length]; + for (int i = 0; i < enumConstants.length; i++) { + String name = names[i]; + if (name == null) { + Enum en = enumConstants[i]; + name = en.toString(); + } + if (useLowerCase) { + name = name.toLowerCase(); + } + textual[i] = config.compileString(name); + } + return construct(enumCls, textual); + } + + /** + * @deprecated since 2.16; use {@link #constructFromToString(MapperConfig, AnnotatedClass)} instead + */ + @Deprecated public static EnumValues constructFromToString(MapperConfig config, Class> enumClass) { Class> cls = ClassUtil.findEnumType(enumClass); @@ -218,7 +256,7 @@ public EnumMap internalMap() { for (Enum en : _values) { map.put(en, _textual[en.ordinal()]); } - result = new EnumMap(map); + _asMap = result = new EnumMap(map); } return result; } diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/EnumSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/EnumSerializationTest.java index d09b3219c5..b9447adff6 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/EnumSerializationTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/EnumSerializationTest.java @@ -296,6 +296,13 @@ public void testEnumsWithJsonProperty() throws Exception { assertEquals(q("aleph"), MAPPER.writeValueAsString(EnumWithJsonProperty.A)); } + public void testEnumsWithJsonPropertyEnableToString() throws Exception { + String result = MAPPER.writerFor(EnumWithJsonProperty.class) + .with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) + .writeValueAsString(EnumWithJsonProperty.A); + assertEquals(q("aleph"), result); + } + // [databind#1535] public void testEnumKeysWithJsonProperty() throws Exception { Map input = new HashMap(); From 450b7e28bb8e32880990834656b52b48cc354b53 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Fri, 14 Jul 2023 20:20:03 -0500 Subject: [PATCH 2/5] fix: use correct value in format visitor --- .../fasterxml/jackson/databind/ser/std/EnumSerializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java index 18e70d2bce..6db368f21b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java @@ -230,8 +230,8 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t // Use toString()? if ((serializers != null) && serializers.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)) { - for (Enum e : _valuesByToString.enums()) { - enums.add(e.toString()); + for (SerializableString value : _valuesByToString.values()) { + enums.add(value.getValue()); } } else { // No, serialize using name() or explicit overrides From 52c6a24aef4a3af14d779fbe37d597ba24b978fb Mon Sep 17 00:00:00 2001 From: iProdigy Date: Fri, 14 Jul 2023 20:21:40 -0500 Subject: [PATCH 3/5] chore: reorder variable declaration in EnumSerializer.construct --- .../com/fasterxml/jackson/databind/ser/std/EnumSerializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java index 6db368f21b..30ba042ead 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java @@ -123,8 +123,8 @@ public static EnumSerializer construct(Class enumClass, SerializationConfig c * handle toString() case dynamically (for example) */ EnumValues v = EnumValues.constructFromName(config, beanDesc.getClassInfo()); - EnumValues valuesByToString = EnumValues.constructFromToString(config, beanDesc.getClassInfo()); EnumValues valuesByEnumNaming = constructEnumNamingStrategyValues(config, (Class>) enumClass, beanDesc.getClassInfo()); + EnumValues valuesByToString = EnumValues.constructFromToString(config, beanDesc.getClassInfo()); Boolean serializeAsIndex = _isShapeWrittenUsingIndex(enumClass, format, true, null); return new EnumSerializer(v, serializeAsIndex, valuesByEnumNaming, valuesByToString); } From 604974006f654f7a91c85be5ec29cd9f69cfe01d Mon Sep 17 00:00:00 2001 From: iProdigy <8106344+iProdigy@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:24:49 -0500 Subject: [PATCH 4/5] docs: linkify Enum#toString Co-authored-by: Kim, Joo Hyuk --- .../com/fasterxml/jackson/databind/ser/std/EnumSerializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java index 30ba042ead..34ea011454 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java @@ -60,7 +60,7 @@ public class EnumSerializer protected final EnumValues _valuesByEnumNaming; /** - * Map that contains pre-resolved values for Enum#toString to use for serialization, + * Map that contains pre-resolved values for {@link Enum#toString} to use for serialization, * while respecting {@link com.fasterxml.jackson.annotation.JsonProperty} * and {@link com.fasterxml.jackson.databind.cfg.EnumFeature#WRITE_ENUMS_TO_LOWERCASE}. * From c41185be2c41b2a107e157093e9bd094c08d6e49 Mon Sep 17 00:00:00 2001 From: iProdigy Date: Fri, 14 Jul 2023 20:25:01 -0500 Subject: [PATCH 5/5] chore: remove unrelated performance optimization --- .../java/com/fasterxml/jackson/databind/util/EnumValues.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java index 91921b18e9..aafc4e775c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java @@ -256,7 +256,7 @@ public EnumMap internalMap() { for (Enum en : _values) { map.put(en, _textual[en.ordinal()]); } - _asMap = result = new EnumMap(map); + result = new EnumMap(map); } return result; }