Skip to content

Commit 01acbc1

Browse files
authored
Fix #5292: support "iPhone params" for Constructors too (#5311)
1 parent f7c7521 commit 01acbc1

File tree

3 files changed

+121
-7
lines changed

3 files changed

+121
-7
lines changed

release-notes/VERSION-2.x

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ Project: jackson-databind
44
=== Releases ===
55
------------------------------------------------------------------------
66

7+
2.20.1 (not yet released)
8+
9+
#5292: `MapperFeature.FIX_FIELD_NAME_UPPER_CASE_PREFIX` does not work with
10+
Constructor parameters
11+
(reported by @bananayong)
12+
713
2.20.0 (28-Aug-2025)
814

915
#2678: `@JacksonInject` added to property overrides value from the JSON

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,20 +1348,22 @@ protected void _fixLeadingFieldNameCase(Map<String, POJOPropertyBuilder> props)
13481348

13491349
// First: find possible candidates where:
13501350
//
1351-
// 1. Property only has Field
1352-
// 2. Field does NOT have explicit name (renaming)
1353-
// 3. Implicit name has upper-case for first and/or second character
1351+
// 1. Property has Field and/or Constructor Parameter
1352+
// 2. Property has no other accessors (no getters/setters)
1353+
// 3. Field/Constructor param does NOT have explicit name (renaming)
1354+
// 4. Implicit name has upper-case for first and/or second character
13541355

13551356
Map<String, POJOPropertyBuilder> fieldsToCheck = null;
13561357
for (Map.Entry<String, POJOPropertyBuilder> entry : props.entrySet()) {
13571358
POJOPropertyBuilder prop = entry.getValue();
13581359

1359-
// First: (1) and (2)
1360-
if (!prop.hasFieldAndNothingElse()
1361-
|| prop.isExplicitlyNamed()) {
1360+
// First: (1), (2) and 3
1361+
if (prop.isExplicitlyNamed() // (3)
1362+
|| !(prop.hasField() || prop.hasConstructorParameter()) // (1)
1363+
|| (prop.hasGetter() || prop.hasSetter())) { // 2
13621364
continue;
13631365
}
1364-
// Second: (3)
1366+
// Second: (4)
13651367
if (!_firstOrSecondCharUpperCase(entry.getKey())) {
13661368
continue;
13671369
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.fasterxml.jackson.databind.misc;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import com.fasterxml.jackson.annotation.JsonCreator;
6+
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
7+
8+
import com.fasterxml.jackson.databind.MapperFeature;
9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
import com.fasterxml.jackson.databind.json.JsonMapper;
11+
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
12+
13+
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
15+
// [databind#5292] Need support for creators `MapperFeature.FIX_FIELD_NAME_UPPER_CASE_PREFIX`
16+
public class IPhoneStyleProperty5292Test
17+
extends DatabindTestUtil
18+
{
19+
static class AppleSingleNonTarget {
20+
private final String name;
21+
22+
public AppleSingleNonTarget(@ImplicitName("name") String name) {
23+
this.name = name;
24+
}
25+
26+
public String getName() {
27+
return name;
28+
}
29+
}
30+
31+
static class AppleSingleIsTarget {
32+
private final String iPhone;
33+
34+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
35+
public AppleSingleIsTarget(@ImplicitName("iPhone") String iPhone) {
36+
this.iPhone = iPhone;
37+
}
38+
39+
public String getIPhone() {
40+
return iPhone;
41+
}
42+
}
43+
44+
// Creator order should be used but just in case, define explicit order
45+
@JsonPropertyOrder({ "iPhone", "name" })
46+
static class AppleDouble {
47+
private final String _iphone;
48+
private final String name;
49+
50+
public AppleDouble(@ImplicitName("iPhone") String iPhone,
51+
@ImplicitName("name") String name) {
52+
this._iphone = iPhone;
53+
this.name = name;
54+
}
55+
56+
public String getIPhone() {
57+
return _iphone;
58+
}
59+
60+
public String getName() {
61+
return name;
62+
}
63+
}
64+
65+
private final ObjectMapper MAPPER = JsonMapper.builder()
66+
.annotationIntrospector(new ImplicitNameIntrospector())
67+
.enable(MapperFeature.FIX_FIELD_NAME_UPPER_CASE_PREFIX)
68+
.build();
69+
70+
@Test
71+
public void testDeserDouble() throws Exception
72+
{
73+
AppleDouble apple = new AppleDouble("iPhone 15", "Jay");
74+
String json = MAPPER.writeValueAsString(apple);
75+
assertEquals("{\"iPhone\":\"iPhone 15\",\"name\":\"Jay\"}", json);
76+
77+
AppleDouble result = MAPPER.readValue(json, AppleDouble.class); // Error thrown
78+
79+
assertEquals("Jay", result.getName());
80+
assertEquals("iPhone 15", result.getIPhone());
81+
}
82+
83+
84+
@Test
85+
public void testSingleArgCase() throws Exception
86+
{
87+
AppleSingleIsTarget apple = new AppleSingleIsTarget("iPhone 15");
88+
String json = MAPPER.writeValueAsString(apple);
89+
assertEquals("{\"iPhone\":\"iPhone 15\"}", json);
90+
91+
AppleSingleIsTarget result = MAPPER.readValue(json, AppleSingleIsTarget.class);
92+
assertEquals("iPhone 15", result.getIPhone());
93+
}
94+
95+
// Just for comparison
96+
@Test
97+
public void testHappyCaseSingleArgString() throws Exception
98+
{
99+
AppleSingleNonTarget apple = new AppleSingleNonTarget("Jay");
100+
String json = MAPPER.writeValueAsString(apple);
101+
assertEquals("{\"name\":\"Jay\"}", json);
102+
103+
AppleSingleNonTarget result = MAPPER.readValue(json, AppleSingleNonTarget.class);
104+
assertEquals("Jay", result.getName());
105+
}
106+
}

0 commit comments

Comments
 (0)