From 188d41da84466787a3d80e762fa44bb5b24e4fa4 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sat, 4 Oct 2025 01:48:11 +0900 Subject: [PATCH 1/6] Fix 3580 --- .../deser/BasicDeserializerFactory.java | 18 ++++- .../databind/deser/std/EnumDeserializer.java | 67 ++++++++++++++++--- .../jackson/databind/util/EnumResolver.java | 48 +++++++++++++ .../format/EnumNumberFormatShape3580Test.java | 66 ++++++++++++++++++ 4 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java index 10c4b68f7c..13f850fa89 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java @@ -1100,7 +1100,8 @@ public JsonDeserializer createEnumDeserializer(DeserializationContext ctxt, config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS), constructEnumNamingStrategyResolver(config, beanDesc.getClassInfo()), // since 2.16 - EnumResolver.constructUsingToString(config, beanDesc.getClassInfo()) + EnumResolver.constructUsingToString(config, beanDesc.getClassInfo()), + constructEnumResolverNumberShape(enumClass, config, beanDesc) ); } } @@ -1815,6 +1816,21 @@ protected EnumResolver constructEnumResolver(Class enumClass, return EnumResolver.constructFor(config, beanDesc.getClassInfo()); } + + protected EnumResolver constructEnumResolverNumberShape(Class enumClass, + DeserializationConfig config, BeanDescription beanDesc) + { + AnnotatedMember jvAcc = beanDesc.findJsonValueAccessor(); + if (jvAcc != null) { + if (config.canOverrideAccessModifiers()) { + ClassUtil.checkAndFixAccess(jvAcc.getMember(), + config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); + } + return EnumResolver.constructUsingNumberShape(config, beanDesc.getClassInfo(), jvAcc); + } + return null; + } + /** * Factory method used to resolve an instance of {@link CompactStringObjectMap} * with {@link EnumNamingStrategy} applied for the target class. diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java index 48c14f1e09..ad74cf0c25 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java @@ -54,8 +54,22 @@ public class EnumDeserializer */ protected volatile CompactStringObjectMap _lookupByToString; + /** + * We may also have integer-type of representation for Enum's, along with `@JsonValue`. + * + * @since 2.20.0 + */ + protected final CompactStringObjectMap _lookupByShapeNumberInt; + protected final Boolean _caseInsensitive; + /** + * Flag to check if FormatShape of int number type would be used to deserialize + * + * @since 2.20.0 + */ + protected final Boolean _isShapeNumberInt; + private Boolean _useDefaultValueForUnknownEnum; private Boolean _useNullForUnknownEnum; @@ -109,15 +123,28 @@ public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, _enumDefaultValue = byNameResolver.getDefaultValue(); _caseInsensitive = caseInsensitive; _isFromIntValue = byNameResolver.isFromIntValue(); + _isShapeNumberInt = null; _lookupByEnumNaming = byEnumNamingResolver == null ? null : byEnumNamingResolver.constructLookup(); _lookupByToString = null; + _lookupByShapeNumberInt = null; } /** * @since 2.16 + * @deprecated since 2.20 */ + @Deprecated public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, EnumResolver byEnumNamingResolver, EnumResolver toStringResolver) + { + this(byNameResolver, caseInsensitive, byEnumNamingResolver, toStringResolver, null); + } + + /** + * @since 2.20 + */ + public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, + EnumResolver byEnumNamingResolver, EnumResolver toStringResolver, EnumResolver shapeNumberResolver) { super(byNameResolver.getEnumClass()); _lookupByName = byNameResolver.constructLookup(); @@ -126,8 +153,10 @@ public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, _enumDefaultValue = byNameResolver.getDefaultValue(); _caseInsensitive = caseInsensitive; _isFromIntValue = byNameResolver.isFromIntValue(); + _isShapeNumberInt = shapeNumberResolver != null; _lookupByEnumNaming = byEnumNamingResolver == null ? null : byEnumNamingResolver.constructLookup(); - _lookupByToString = toStringResolver == null ? null : toStringResolver.constructLookup(); + _lookupByToString = _isShapeNumberInt ? toStringResolver.constructLookup() : null; + _lookupByShapeNumberInt = shapeNumberResolver == null ? null : shapeNumberResolver.constructLookup(); } /** @@ -143,10 +172,12 @@ protected EnumDeserializer(EnumDeserializer base, Boolean caseInsensitive, _enumDefaultValue = base._enumDefaultValue; _caseInsensitive = Boolean.TRUE.equals(caseInsensitive); _isFromIntValue = base._isFromIntValue; + _isShapeNumberInt = base._isShapeNumberInt; _useDefaultValueForUnknownEnum = useDefaultValueForUnknownEnum; _useNullForUnknownEnum = useNullForUnknownEnum; _lookupByEnumNaming = base._lookupByEnumNaming; _lookupByToString = base._lookupByToString; + _lookupByShapeNumberInt = base._lookupByShapeNumberInt; } /** @@ -238,7 +269,7 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, JsonFormat.Feature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)).orElse(_useDefaultValueForUnknownEnum); Boolean useNullForUnknownEnum = Optional.ofNullable(findFormatFeature(ctxt, property, handledType(), JsonFormat.Feature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)).orElse(_useNullForUnknownEnum); - + return withResolved(caseInsensitive, useDefaultValueForUnknownEnum, useNullForUnknownEnum); } @@ -280,6 +311,10 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx // 26-Sep-2021, tatu: [databind#1850] Special case where we get "true" integer // enumeration and should avoid use of {@code Enum.index()} if (_isFromIntValue) { + // [databind#3580] Enum (de)serialization in conjunction with JsonFormat.Shape.NUMBER_INT + if (_isShapeNumberInt) { + return _fromInteger(p, ctxt, p.getIntValue()); + } // ... whether to rely on "getText()" returning String, or get number, convert? // For now assume all format backends can produce String: return _fromString(p, ctxt, p.getText()); @@ -327,7 +362,7 @@ private CompactStringObjectMap _resolveCurrentLookup(DeserializationContext ctxt } protected Object _fromInteger(JsonParser p, DeserializationContext ctxt, - int index) + int intValue) throws IOException { final CoercionAction act = ctxt.findCoercionAction(logicalType(), handledType(), @@ -336,13 +371,13 @@ protected Object _fromInteger(JsonParser p, DeserializationContext ctxt, // First, check legacy setting for slightly different message if (act == CoercionAction.Fail) { if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) { - return ctxt.handleWeirdNumberValue(_enumClass(), index, + return ctxt.handleWeirdNumberValue(_enumClass(), intValue, "not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow" ); } // otherwise this will force failure with new setting - _checkCoercionFail(ctxt, act, handledType(), index, - "Integer value ("+index+")"); + _checkCoercionFail(ctxt, act, handledType(), intValue, + "Integer value ("+intValue+")"); } switch (act) { case AsNull: @@ -352,14 +387,26 @@ protected Object _fromInteger(JsonParser p, DeserializationContext ctxt, case TryConvert: default: } - if (index >= 0 && index < _enumsByIndex.length) { - return _enumsByIndex[index]; + + // [databind#3580] Enum (de)serialization in conjunction with JsonFormat.Shape.NUMBER_INT + if (_isShapeNumberInt) { + Object numberShape = _lookupByShapeNumberInt.find(String.valueOf(intValue)); + if (numberShape != null) { + return numberShape; + } else { + return ctxt.handleWeirdNumberValue(_enumClass(), intValue, + "Number Int value is not one of expected values", + _lookupByShapeNumberInt.toString()); + } + } + if (intValue >= 0 && intValue < _enumsByIndex.length) { + return _enumsByIndex[intValue]; } if (useDefaultValueForUnknownEnum(ctxt)) { return _enumDefaultValue; } if (!useNullForUnknownEnum(ctxt)) { - return ctxt.handleWeirdNumberValue(_enumClass(), index, + return ctxt.handleWeirdNumberValue(_enumClass(), intValue, "index value outside legal index range [0..%s]", _enumsByIndex.length-1); } @@ -470,7 +517,7 @@ protected Class _enumClass() { /** * Since 2.16, {@link #_lookupByToString} it is passed via - * {@link #EnumDeserializer(EnumResolver, boolean, EnumResolver, EnumResolver)}, so there is no need for lazy + * {@link #EnumDeserializer(EnumResolver, boolean, EnumResolver, EnumResolver, EnumResolver)}, so there is no need for lazy * initialization. But kept for backward-compatilibility reasons. In case {@link #_lookupByToString} is null. * * @deprecated Since 2.16 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 3e011bdeb4..40f62b4a33 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java @@ -2,6 +2,7 @@ import java.util.*; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.AnnotationIntrospector; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.EnumNamingStrategy; @@ -302,6 +303,53 @@ public static EnumResolver constructUsingMethod(DeserializationConfig config, ); } + /** + * Method used when ALL of conditions below are met + *

+ * 1. actual String serialization is indicated using @JsonValue on a method in Enum class AND + * 2. Enum class is annotated with `@JsonFormat` + * + * @since 2.20 + */ + public static EnumResolver constructUsingNumberShape(DeserializationConfig config, AnnotatedClass annotatedClass, AnnotatedMember accessor) + { + // prepare data + final AnnotationIntrospector ai = config.getAnnotationIntrospector(); + final boolean isIgnoreCase = config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS); + final Class enumCls0 = annotatedClass.getRawType(); + final Class> enumCls = _enumClass(enumCls0); + final Enum[] enumConstants = _enumConstants(enumCls0); + HashMap> map = new HashMap<>(); + + JsonFormat.Value format = ai.findFormat(annotatedClass); + if (format == null) { + return null; + } + if (format.getShape() != JsonFormat.Shape.NUMBER_INT) { + throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "); + } + + // from last to first, so that in case of duplicate values, first wins + for (int i = enumConstants.length; --i >= 0; ) { + Enum en = enumConstants[i]; + try { + Object o = accessor.getValue(en); + if (o != null) { + map.put(o.toString(), en); + } + } catch (Exception e) { + throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage()); + } + } + return new EnumResolver(enumCls, enumConstants, map, + _enumDefault(ai, annotatedClass, enumConstants), + isIgnoreCase, + // 26-Sep-2021, tatu: [databind#1850] Need to consider "from int" case + _isIntType(accessor.getRawType()), + true + ); + } + public CompactStringObjectMap constructLookup() { return CompactStringObjectMap.construct(_enumsById); } diff --git a/src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java b/src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java new file mode 100644 index 0000000000..33b40d5003 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java @@ -0,0 +1,66 @@ +package com.fasterxml.jackson.databind.format; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; +import org.junit.jupiter.api.Test; + +import javax.imageio.metadata.IIOInvalidTreeException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// [databind#3580] Enum (de)serialization in conjunction with JsonFormat.Shape.NUMBER_INT +public class EnumNumberFormatShape3580Test + extends DatabindTestUtil +{ + public static class Pojo { + public State state; + + public Pojo() {} + + public Pojo(State state) { + this.state = state; + } + } + + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) + public enum State { + OFF(17), + ON(31), + UNKNOWN(99); + + private int value; + + State(int value) { + this.value = value; + } + + @JsonValue + public int value() { + return this.value; + } + } + + @Test + public void testEnumNumberFormatShape3580() + throws Exception { + ObjectMapper mapper = JsonMapper.builder().build(); + + // Serialize + assertEquals("{\"state\":17}", mapper.writeValueAsString(new Pojo(State.OFF))); // + assertEquals("{\"state\":31}", mapper.writeValueAsString(new Pojo(State.ON))); // + assertEquals("{\"state\":99}", mapper.writeValueAsString(new Pojo(State.UNKNOWN))); // + + // Pass Deserialize + assertEquals(State.OFF, mapper.readValue("{\"state\":17}", Pojo.class).state); // Pojo[state=OFF] + assertEquals(State.ON, mapper.readValue("{\"state\":31}", Pojo.class).state); // Pojo[state=OFF] + assertEquals(State.UNKNOWN, mapper.readValue("{\"state\":99}", Pojo.class).state); // Pojo[state=OFF] + + // Fail : Try to use ordinal number + assertThrows(InvalidFormatException.class, () -> mapper.readValue("{\"state\":0}", Pojo.class)); + } +} From bd241e33e95bbebd4cd0afd2a632a801b91a7ce0 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sat, 4 Oct 2025 01:52:05 +0900 Subject: [PATCH 2/6] Add more test wrt #3580 --- .../EnumNumberFormatShapeRecord3580Test.java | 53 +++++++++++++++++++ .../format/EnumNumberFormatShape3580Test.java | 44 +++++++-------- 2 files changed, 71 insertions(+), 26 deletions(-) create mode 100644 src/test-jdk17/java/com/fasterxml/jackson/databind/records/EnumNumberFormatShapeRecord3580Test.java diff --git a/src/test-jdk17/java/com/fasterxml/jackson/databind/records/EnumNumberFormatShapeRecord3580Test.java b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/EnumNumberFormatShapeRecord3580Test.java new file mode 100644 index 0000000000..672a0ae396 --- /dev/null +++ b/src/test-jdk17/java/com/fasterxml/jackson/databind/records/EnumNumberFormatShapeRecord3580Test.java @@ -0,0 +1,53 @@ +package com.fasterxml.jackson.databind.records; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +// [databind#3580] Enum (de)serialization in conjunction with JsonFormat.Shape.NUMBER_INT +public class EnumNumberFormatShapeRecord3580Test + extends DatabindTestUtil +{ + public record Record3580(@JsonFormat(shape = JsonFormat.Shape.NUMBER) RecordState3580 state) {} + + public enum RecordState3580 { + OFF(17), + ON(31), + UNKNOWN(99); + + private int value; + + RecordState3580(int value) { this.value = value; } + + @JsonValue + public int value() {return this.value;} + } + + @Test + public void testEnumNumberFormatShapeRecord3580() + throws Exception + { + ObjectMapper mapper = JsonMapper.builder().build(); + + // Serialize + assertEquals("{\"state\":17}", mapper.writeValueAsString(new Record3580(RecordState3580.OFF))); // + assertEquals("{\"state\":31}", mapper.writeValueAsString(new Record3580(RecordState3580.ON))); // + assertEquals("{\"state\":99}", mapper.writeValueAsString(new Record3580(RecordState3580.UNKNOWN))); // + + // Pass Deserialize + assertEquals(RecordState3580.OFF, mapper.readValue("{\"state\":17}", Record3580.class).state); // Pojo[state=OFF] + assertEquals(RecordState3580.ON, mapper.readValue("{\"state\":31}", Record3580.class).state); // Pojo[state=OFF] + assertEquals(RecordState3580.UNKNOWN, mapper.readValue("{\"state\":99}", Record3580.class).state); // Pojo[state=OFF] + + // Fail : Try to use ordinal number + assertThrows(InvalidFormatException.class, () -> mapper.readValue("{\"state\":0}", Record3580.class)); + } +} diff --git a/src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java b/src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java index 33b40d5003..f0926ba872 100644 --- a/src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/format/EnumNumberFormatShape3580Test.java @@ -6,61 +6,53 @@ import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; -import org.junit.jupiter.api.Test; -import javax.imageio.metadata.IIOInvalidTreeException; +import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; // [databind#3580] Enum (de)serialization in conjunction with JsonFormat.Shape.NUMBER_INT public class EnumNumberFormatShape3580Test - extends DatabindTestUtil + extends DatabindTestUtil { - public static class Pojo { - public State state; - - public Pojo() {} - - public Pojo(State state) { - this.state = state; - } + public static class Pojo3580 { + public PojoState3580 state; + public Pojo3580() {} + public Pojo3580(PojoState3580 state) {this.state = state;} } @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) - public enum State { + public enum PojoState3580 { OFF(17), ON(31), UNKNOWN(99); private int value; - State(int value) { - this.value = value; - } + PojoState3580(int value) { this.value = value; } @JsonValue - public int value() { - return this.value; - } + public int value() {return this.value;} } @Test public void testEnumNumberFormatShape3580() - throws Exception { + throws Exception + { ObjectMapper mapper = JsonMapper.builder().build(); // Serialize - assertEquals("{\"state\":17}", mapper.writeValueAsString(new Pojo(State.OFF))); // - assertEquals("{\"state\":31}", mapper.writeValueAsString(new Pojo(State.ON))); // - assertEquals("{\"state\":99}", mapper.writeValueAsString(new Pojo(State.UNKNOWN))); // + assertEquals("{\"state\":17}", mapper.writeValueAsString(new Pojo3580(PojoState3580.OFF))); // + assertEquals("{\"state\":31}", mapper.writeValueAsString(new Pojo3580(PojoState3580.ON))); // + assertEquals("{\"state\":99}", mapper.writeValueAsString(new Pojo3580(PojoState3580.UNKNOWN))); // // Pass Deserialize - assertEquals(State.OFF, mapper.readValue("{\"state\":17}", Pojo.class).state); // Pojo[state=OFF] - assertEquals(State.ON, mapper.readValue("{\"state\":31}", Pojo.class).state); // Pojo[state=OFF] - assertEquals(State.UNKNOWN, mapper.readValue("{\"state\":99}", Pojo.class).state); // Pojo[state=OFF] + assertEquals(PojoState3580.OFF, mapper.readValue("{\"state\":17}", Pojo3580.class).state); // Pojo[state=OFF] + assertEquals(PojoState3580.ON, mapper.readValue("{\"state\":31}", Pojo3580.class).state); // Pojo[state=OFF] + assertEquals(PojoState3580.UNKNOWN, mapper.readValue("{\"state\":99}", Pojo3580.class).state); // Pojo[state=OFF] // Fail : Try to use ordinal number - assertThrows(InvalidFormatException.class, () -> mapper.readValue("{\"state\":0}", Pojo.class)); + assertThrows(InvalidFormatException.class, () -> mapper.readValue("{\"state\":0}", Pojo3580.class)); } } From ac68a08afdc4b3a38cba6aa1d45dcabe623de311 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sat, 4 Oct 2025 02:05:18 +0900 Subject: [PATCH 3/6] Fix NPE --- .../fasterxml/jackson/databind/deser/std/EnumDeserializer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java index ad74cf0c25..f3de166dd9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java @@ -338,6 +338,9 @@ protected Object _fromString(JsonParser p, DeserializationContext ctxt, throws IOException { CompactStringObjectMap lookup = _resolveCurrentLookup(ctxt); + if (lookup == null) { + return null; + } Object result = lookup.find(text); if (result == null) { String trimmed = text.trim(); From a9466e035f88662817c7120956e7f8373d3d8518 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sat, 4 Oct 2025 02:09:18 +0900 Subject: [PATCH 4/6] Revert "Fix NPE" This reverts commit ac68a08afdc4b3a38cba6aa1d45dcabe623de311. --- .../fasterxml/jackson/databind/deser/std/EnumDeserializer.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java index f3de166dd9..ad74cf0c25 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java @@ -338,9 +338,6 @@ protected Object _fromString(JsonParser p, DeserializationContext ctxt, throws IOException { CompactStringObjectMap lookup = _resolveCurrentLookup(ctxt); - if (lookup == null) { - return null; - } Object result = lookup.find(text); if (result == null) { String trimmed = text.trim(); From 43388038cca5e472e05e543e63d75f8660ee4f83 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sat, 4 Oct 2025 02:10:02 +0900 Subject: [PATCH 5/6] Fix accidental toString resolver edit --- .../fasterxml/jackson/databind/deser/std/EnumDeserializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java index ad74cf0c25..5a1c22168d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java @@ -155,7 +155,7 @@ public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, _isFromIntValue = byNameResolver.isFromIntValue(); _isShapeNumberInt = shapeNumberResolver != null; _lookupByEnumNaming = byEnumNamingResolver == null ? null : byEnumNamingResolver.constructLookup(); - _lookupByToString = _isShapeNumberInt ? toStringResolver.constructLookup() : null; + _lookupByToString = toStringResolver == null ? null : toStringResolver.constructLookup(); _lookupByShapeNumberInt = shapeNumberResolver == null ? null : shapeNumberResolver.constructLookup(); } From ddc0dedcf806b39545d58ceae9559ea901389227 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sat, 4 Oct 2025 19:37:53 +0900 Subject: [PATCH 6/6] Fix versions --- .../jackson/databind/deser/std/EnumDeserializer.java | 12 ++++++------ .../jackson/databind/util/EnumResolver.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java index 5a1c22168d..a3f91fbcdd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java @@ -66,9 +66,9 @@ public class EnumDeserializer /** * Flag to check if FormatShape of int number type would be used to deserialize * - * @since 2.20.0 + * @since 2.21.0 */ - protected final Boolean _isShapeNumberInt; + protected final boolean _isShapeNumberInt; private Boolean _useDefaultValueForUnknownEnum; private Boolean _useNullForUnknownEnum; @@ -85,7 +85,7 @@ public class EnumDeserializer * Marker flag that indicates whether the Enum class has {@code @JsonValue} * annotated accessor (or equivalent), used to populate {@link #_lookupByName}. * - * @since 2.20 + * @since 2.21.0 */ protected final boolean _hasAsValueAnnotation; @@ -123,7 +123,7 @@ public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, _enumDefaultValue = byNameResolver.getDefaultValue(); _caseInsensitive = caseInsensitive; _isFromIntValue = byNameResolver.isFromIntValue(); - _isShapeNumberInt = null; + _isShapeNumberInt = false; _lookupByEnumNaming = byEnumNamingResolver == null ? null : byEnumNamingResolver.constructLookup(); _lookupByToString = null; _lookupByShapeNumberInt = null; @@ -131,7 +131,7 @@ public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, /** * @since 2.16 - * @deprecated since 2.20 + * @deprecated since 2.21.0 */ @Deprecated public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, @@ -141,7 +141,7 @@ public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, } /** - * @since 2.20 + * @since 2.21.0 */ public EnumDeserializer(EnumResolver byNameResolver, boolean caseInsensitive, EnumResolver byEnumNamingResolver, EnumResolver toStringResolver, EnumResolver shapeNumberResolver) 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 40f62b4a33..d645d0b387 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java @@ -309,7 +309,7 @@ public static EnumResolver constructUsingMethod(DeserializationConfig config, * 1. actual String serialization is indicated using @JsonValue on a method in Enum class AND * 2. Enum class is annotated with `@JsonFormat` * - * @since 2.20 + * @since 2.21.0 */ public static EnumResolver constructUsingNumberShape(DeserializationConfig config, AnnotatedClass annotatedClass, AnnotatedMember accessor) {