Skip to content

Commit baf2388

Browse files
cowtowncoderclaude
andcommitted
Merge branch '2.x' into 3.1
Port #6210 fix (`EnumResolver.constructUsingMethod()`) and test to `tools.jackson`; add 3.1.9 release notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 parents 5700ac2 + 6d0a92a commit baf2388

6 files changed

Lines changed: 176 additions & 1 deletion

File tree

‎release-notes/CREDITS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,3 +493,7 @@ Anna Breeva (@ab310013)
493493
* Reported #6342: `@JsonFormat(shape = NUMBER)` on primitive `boolean` getter written
494494
as `true`/`false` with Blackbird / Afterburner
495495
[3.1.9]
496+
497+
@bugada
498+
* Reported #6210: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
499+
[3.1.9]

‎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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ No changes since 3.1
1515
(nor with Builders, `@JsonUnwrapped`, external type ids or `Shape.ARRAY`)
1616
(reported by João G)
1717
(fix by João G and @cowtowncoder, w/ Claude code)
18+
#6210: `@JsonAlias` ignored on Enum values if `@JsonValue` is present
19+
(reported by @bugada)
20+
(fix by @pjfanning, w/ Claude code)
1821
#6295: `@JsonInclude(NON_DEFAULT)` omits a changed array property when the default
1922
array contains `null`
2023
(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
@@ -248,7 +248,15 @@ public static EnumResolver constructUsingMethod(DeserializationConfig config,
248248
final Class<Enum<?>> enumCls = _enumClass(enumCls0);
249249
final Enum<?>[] enumConstants = _enumConstants(enumCls);
250250
final Enum<?> defaultEnum = _enumDefault(config, annotatedClass, enumConstants);
251-
251+
252+
// introspect
253+
// 10-Sep-2026, pjfanning: [databind#6210] Aliases need to be honored here too
254+
final String[][] allAliases = new String[enumConstants.length][];
255+
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
256+
if (ai != null) {
257+
ai.findEnumAliases(config, annotatedClass, enumConstants, allAliases);
258+
}
259+
252260
// build
253261
HashMap<String, Enum<?>> map = new HashMap<>();
254262
// from last to first, so that in case of duplicate values, first wins
@@ -263,6 +271,16 @@ public static EnumResolver constructUsingMethod(DeserializationConfig config,
263271
throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage());
264272
}
265273
}
274+
// Aliases added after all primary ids (which they must not override),
275+
// from first to last so that in case of duplicate aliases, first wins
276+
for (int i = 0, len = enumConstants.length; i < len; ++i) {
277+
String[] aliases = allAliases[i];
278+
if (aliases != null) {
279+
for (String alias : aliases) {
280+
map.putIfAbsent(alias, enumConstants[i]);
281+
}
282+
}
283+
}
266284
return new EnumResolver(enumCls, enumConstants, map,
267285
defaultEnum, isIgnoreCase,
268286
// 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)