Skip to content

Commit 24f5d33

Browse files
cowtowncoderclaude
andcommitted
Merge branch '3.1' into 3.2
Add 3.2.5 release notes for #6210. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 parents 9762352 + baf2388 commit 24f5d33

6 files changed

Lines changed: 179 additions & 1 deletion

File tree

‎release-notes/CREDITS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,3 +727,7 @@ Anna Breeva (@ab310013)
727727
* Reported #6342: `@JsonFormat(shape = NUMBER)` on primitive `boolean` getter written
728728
as `true`/`false` with Blackbird / Afterburner
729729
[3.2.5]
730+
731+
@bugada
732+
* Reported #6210: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
733+
[3.2.5]

‎release-notes/CREDITS-2.x‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,3 +2158,7 @@ Anna Breeva (@ab310013)
21582158
* Reported #6315: Map key deserializers should apply same type checks as bean
21592159
deserializers
21602160
(2.18.13)
2161+
2162+
@bugada
2163+
* Reported #6210: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
2164+
[2.23.0]

‎release-notes/VERSION‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ No changes since 3.2
1111

1212
3.2.5 (not yet released)
1313

14+
#6210: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
15+
(reported by @bugada)
16+
(fix by @pjfanning, w/ Claude code)
1417
#6295: `@JsonInclude(NON_DEFAULT)` omits a changed array property when the default
1518
array contains `null`
1619
(reported, fixed by @Marti2203)
@@ -422,6 +425,9 @@ No changes since 3.2
422425

423426
3.1.9 (not yet released)
424427

428+
#6210: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
429+
(reported by @bugada)
430+
(fix by @pjfanning, w/ Claude code)
425431
#6295: `@JsonInclude(NON_DEFAULT)` omits a changed array property when the default
426432
array contains `null`
427433
(reported, fixed by @Marti2203)

‎release-notes/VERSION-2.x‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Project: jackson-databind
1010
(nor with Builders, `@JsonUnwrapped`, external type ids or `Shape.ARRAY`)
1111
(reported by João G)
1212
(fix by João G, @cowtowncoder; backport by @Dongnyoung)
13+
#6210: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
14+
(reported by @bugada)
15+
(fix by @pjfanning, w/ Claude code)
1316
#6240: `@JsonValue` ignored on getter also annotated with `@JsonKey`
1417
(fix by @kimsanhaa)
1518
#6261: Support `@JsonView` on properties-based Creator parameters of Enum, Map and EnumMap types
@@ -140,6 +143,9 @@ Project: jackson-databind
140143
#6315: Map key deserializers should apply same type checks as bean deserializers
141144
(reported by @ab310013)
142145
(fix by @pjfanning)
146+
#6329: Add JDK-bundled `RowSet` gadget siblings of `JdbcRowSetImpl` to
147+
deserialization deny-list (`CachedRowSetImpl`, `WebRowSetImpl`,
148+
`FilteredRowSetImpl`, `JoinRowSetImpl`)
143149

144150
2.21.8 (17-Oct-2026)
145151

‎src/main/java/tools/jackson/databind/util/EnumResolver.java‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,15 @@ public static EnumResolver constructUsingMethod(DeserializationConfig config,
292292
final Class<Enum<?>> enumCls = _enumClass(enumCls0);
293293
final Enum<?>[] enumConstants = _enumConstants(enumCls);
294294
final Enum<?> defaultEnum = _enumDefault(config, annotatedClass, enumConstants);
295-
295+
296+
// introspect
297+
// 10-Sep-2026, pjfanning: [databind#6210] Aliases need to be honored here too
298+
final String[][] allAliases = new String[enumConstants.length][];
299+
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
300+
if (ai != null) {
301+
ai.findEnumAliases(config, annotatedClass, enumConstants, allAliases);
302+
}
303+
296304
// build
297305
HashMap<String, Enum<?>> map = new HashMap<>();
298306
// from last to first, so that in case of duplicate values, first wins
@@ -307,6 +315,16 @@ public static EnumResolver constructUsingMethod(DeserializationConfig config,
307315
throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage());
308316
}
309317
}
318+
// Aliases added after all primary ids (which they must not override),
319+
// from first to last so that in case of duplicate aliases, first wins
320+
for (int i = 0, len = enumConstants.length; i < len; ++i) {
321+
String[] aliases = allAliases[i];
322+
if (aliases != null) {
323+
for (String alias : aliases) {
324+
map.putIfAbsent(alias, enumConstants[i]);
325+
}
326+
}
327+
}
310328
return new EnumResolver(enumCls, enumConstants, map,
311329
defaultEnum, isIgnoreCase,
312330
// 26-Sep-2021, tatu: [databind#1850] Need to consider "from int" case
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package tools.jackson.databind.deser.enums;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import com.fasterxml.jackson.annotation.JsonAlias;
6+
import com.fasterxml.jackson.annotation.JsonValue;
7+
8+
import tools.jackson.databind.*;
9+
10+
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
12+
import static tools.jackson.databind.testutil.DatabindTestUtil.a2q;
13+
import static tools.jackson.databind.testutil.DatabindTestUtil.newJsonMapper;
14+
import static tools.jackson.databind.testutil.DatabindTestUtil.q;
15+
16+
// [databind#6210]: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
17+
public class EnumAliasWithJsonValue6210Test
18+
{
19+
enum Versus6210 {
20+
@JsonAlias({ "A", "B" })
21+
BUY("B"),
22+
@JsonAlias({ "V", "S" })
23+
SELL("S");
24+
25+
private final String value;
26+
27+
Versus6210(String value) {
28+
this.value = value;
29+
}
30+
31+
@JsonValue
32+
public String getValue() {
33+
return value;
34+
}
35+
}
36+
37+
// Aliases that collide with `@JsonValue` ids of other values, and each other
38+
enum Colliding6210 {
39+
@JsonAlias({ "b" })
40+
ALPHA("a"),
41+
@JsonAlias({ "a", "x" })
42+
BETA("b"),
43+
@JsonAlias({ "x" })
44+
GAMMA("c");
45+
46+
private final String value;
47+
48+
Colliding6210(String value) {
49+
this.value = value;
50+
}
51+
52+
@JsonValue
53+
public String getValue() {
54+
return value;
55+
}
56+
}
57+
58+
enum IntValued6210 {
59+
@JsonAlias({ "one" })
60+
FIRST(1),
61+
@JsonAlias({ "two", "second" })
62+
SECOND(2);
63+
64+
private final int value;
65+
66+
IntValued6210(int value) {
67+
this.value = value;
68+
}
69+
70+
@JsonValue
71+
public int getValue() {
72+
return value;
73+
}
74+
}
75+
76+
/*
77+
/**********************************************************************
78+
/* Test methods
79+
/**********************************************************************
80+
*/
81+
82+
private final ObjectMapper MAPPER = newJsonMapper();
83+
84+
// [databind#6210]
85+
@Test
86+
public void aliasesWithJsonValue() throws Exception {
87+
ObjectReader r = MAPPER.readerFor(Versus6210.class);
88+
89+
// First: values from `@JsonValue` accessor still work
90+
assertEquals(Versus6210.BUY, r.readValue(q("B")));
91+
assertEquals(Versus6210.SELL, r.readValue(q("S")));
92+
93+
// and then aliases, which used to fail
94+
assertEquals(Versus6210.BUY, r.readValue(q("A")));
95+
assertEquals(Versus6210.SELL, r.readValue(q("V")));
96+
}
97+
98+
// [databind#6210]: aliases must not override `@JsonValue`-provided ids
99+
@Test
100+
public void jsonValueWinsOverAlias() throws Exception {
101+
ObjectReader r = MAPPER.readerFor(Colliding6210.class);
102+
103+
// "a" is id of ALPHA, alias of BETA; "b" id of BETA, alias of ALPHA
104+
assertEquals(Colliding6210.ALPHA, r.readValue(q("a")));
105+
assertEquals(Colliding6210.BETA, r.readValue(q("b")));
106+
assertEquals(Colliding6210.GAMMA, r.readValue(q("c")));
107+
}
108+
109+
// [databind#6210]: with duplicate aliases, first Enum value wins
110+
// (same as without `@JsonValue`)
111+
@Test
112+
public void firstAliasWinsForDuplicates() throws Exception {
113+
assertEquals(Colliding6210.BETA, MAPPER.readValue(q("x"), Colliding6210.class));
114+
}
115+
116+
// [databind#6210]: aliases are Strings even if `@JsonValue` is of int type
117+
@Test
118+
public void aliasesWithIntJsonValue() throws Exception {
119+
ObjectReader r = MAPPER.readerFor(IntValued6210.class);
120+
121+
assertEquals(IntValued6210.FIRST, r.readValue("1"));
122+
assertEquals(IntValued6210.SECOND, r.readValue("2"));
123+
124+
assertEquals(IntValued6210.FIRST, r.readValue(q("one")));
125+
assertEquals(IntValued6210.SECOND, r.readValue(q("two")));
126+
assertEquals(IntValued6210.SECOND, r.readValue(q("second")));
127+
}
128+
129+
// [databind#6210]: also verify Enum-as-Map-key path
130+
@Test
131+
public void aliasesWithJsonValueAsMapKey() throws Exception {
132+
java.util.Map<Versus6210, String> map = MAPPER.readValue(
133+
a2q("{'V':'x', 'B':'y'}"),
134+
MAPPER.getTypeFactory().constructMapType(java.util.LinkedHashMap.class,
135+
Versus6210.class, String.class));
136+
assertEquals(2, map.size());
137+
assertEquals("x", map.get(Versus6210.SELL));
138+
assertEquals("y", map.get(Versus6210.BUY));
139+
}
140+
}

0 commit comments

Comments
 (0)