Skip to content

Commit 66bef09

Browse files
authored
@JsonbCreator uses PropertyNamingStrategy. (#584)
Signed-off-by: Benjamin Marwell <[email protected]>
1 parent 3a5b85a commit 66bef09

File tree

8 files changed

+107
-33
lines changed

8 files changed

+107
-33
lines changed

src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import jakarta.json.bind.annotation.JsonbTypeInfo;
6060
import jakarta.json.bind.annotation.JsonbTypeSerializer;
6161
import jakarta.json.bind.annotation.JsonbVisibility;
62+
import jakarta.json.bind.config.PropertyNamingStrategy;
6263
import jakarta.json.bind.config.PropertyVisibilityStrategy;
6364
import jakarta.json.bind.serializer.JsonbDeserializer;
6465
import jakarta.json.bind.serializer.JsonbSerializer;
@@ -152,10 +153,12 @@ private String getJsonbPropertyCustomizedName(Property property, JsonbAnnotatedE
152153
/**
153154
* Searches for JsonbCreator annotation on constructors and static methods.
154155
*
155-
* @param clazz class to search
156+
* @param clazz class to search
157+
* @param propertyNamingStrategy The naming strategy to use for the ${@code JsonbConstructor} annotation,
158+
* if set and no {@code JsonbProperty} annotations are present.
156159
* @return JsonbCreator metadata object
157160
*/
158-
public JsonbCreator getCreator(Class<?> clazz) {
161+
public JsonbCreator getCreator(Class<?> clazz, PropertyNamingStrategy propertyNamingStrategy) {
159162
JsonbCreator jsonbCreator = null;
160163
Constructor<?>[] declaredConstructors =
161164
AccessController.doPrivileged((PrivilegedAction<Constructor<?>[]>) clazz::getDeclaredConstructors);
@@ -164,7 +167,7 @@ public JsonbCreator getCreator(Class<?> clazz) {
164167
final jakarta.json.bind.annotation.JsonbCreator annot = findAnnotation(constructor.getDeclaredAnnotations(),
165168
jakarta.json.bind.annotation.JsonbCreator.class);
166169
if (annot != null) {
167-
jsonbCreator = createJsonbCreator(constructor, jsonbCreator, clazz);
170+
jsonbCreator = createJsonbCreator(constructor, jsonbCreator, clazz, propertyNamingStrategy);
168171
}
169172
}
170173

@@ -179,19 +182,19 @@ public JsonbCreator getCreator(Class<?> clazz) {
179182
method,
180183
clazz));
181184
}
182-
jsonbCreator = createJsonbCreator(method, jsonbCreator, clazz);
185+
jsonbCreator = createJsonbCreator(method, jsonbCreator, clazz, propertyNamingStrategy);
183186
}
184187
}
185188
if (jsonbCreator == null) {
186-
jsonbCreator = ClassMultiReleaseExtension.findCreator(clazz, declaredConstructors, this);
189+
jsonbCreator = ClassMultiReleaseExtension.findCreator(clazz, declaredConstructors, this, propertyNamingStrategy);
187190
if (jsonbCreator == null) {
188191
jsonbCreator = constructorPropertiesIntrospector.getCreator(declaredConstructors);
189192
}
190193
}
191194
return jsonbCreator;
192195
}
193196

194-
JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Class<?> clazz) {
197+
JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Class<?> clazz, PropertyNamingStrategy propertyNamingStrategy) {
195198
if (existing != null) {
196199
throw new JsonbException(Messages.getMessage(MessageKeys.MULTIPLE_JSONB_CREATORS, clazz));
197200
}
@@ -205,7 +208,8 @@ JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Cl
205208
if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isEmpty()) {
206209
creatorModels[i] = new CreatorModel(jsonbPropertyAnnotation.value(), parameter, executable, jsonbContext);
207210
} else {
208-
creatorModels[i] = new CreatorModel(parameter.getName(), parameter, executable, jsonbContext);
211+
final String translatedParameterName = propertyNamingStrategy.translateName(parameter.getName());
212+
creatorModels[i] = new CreatorModel(translatedParameterName, parameter, executable, jsonbContext);
209213
}
210214
}
211215

@@ -779,16 +783,19 @@ public Set<Class<?>> collectInterfaces(Class<?> cls) {
779783
/**
780784
* Processes customizations.
781785
*
782-
* @param clsElement Element to process.
786+
* @param clsElement Element to process.
787+
* @param propertyNamingStrategy The naming strategy to use for the ${@code JsonbConstructor} annotation,
788+
* if set and no {@code JsonbProperty} annotations are present.
783789
* @return Populated {@link ClassCustomization} instance.
784790
*/
785791
public ClassCustomization introspectCustomization(JsonbAnnotatedElement<Class<?>> clsElement,
786-
ClassCustomization parentCustomization) {
792+
ClassCustomization parentCustomization,
793+
PropertyNamingStrategy propertyNamingStrategy) {
787794
return ClassCustomization.builder()
788795
.nillable(isClassNillable(clsElement))
789796
.dateTimeFormatter(getJsonbDateFormat(clsElement))
790797
.numberFormatter(getJsonbNumberFormat(clsElement))
791-
.creator(getCreator(clsElement.getElement()))
798+
.creator(getCreator(clsElement.getElement(), propertyNamingStrategy))
792799
.propertyOrder(getPropertyOrder(clsElement))
793800
.adapterBinding(getAdapterBinding(clsElement))
794801
.serializerBinding(getSerializerBinding(clsElement))

src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Optional;
1919

2020
import jakarta.json.bind.JsonbException;
21+
import jakarta.json.bind.config.PropertyNamingStrategy;
2122

2223
import org.eclipse.yasson.internal.model.JsonbCreator;
2324
import org.eclipse.yasson.internal.model.Property;
@@ -42,7 +43,8 @@ static boolean isSpecialAccessorMethod(Method method, Map<String, Property> clas
4243

4344
static JsonbCreator findCreator(Class<?> clazz,
4445
Constructor<?>[] declaredConstructors,
45-
AnnotationIntrospector introspector) {
46+
AnnotationIntrospector introspector,
47+
PropertyNamingStrategy propertyNamingStrategy) {
4648
return null;
4749
}
4850

src/main/java/org/eclipse/yasson/internal/MappingContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ private static Function<Class<?>, ClassModel> createParseClassModelFunction(Clas
8888
.introspectCustomization(clsElement,
8989
parentClassModel == null
9090
? ClassCustomization.empty()
91-
: parentClassModel.getClassCustomization());
91+
: parentClassModel.getClassCustomization(),
92+
jsonbContext.getConfigProperties().getPropertyNamingStrategy());
9293
// PolymorphismSupport configPolymorphism = jsonbContext.getConfigProperties().getPolymorphismSupport();
9394
// if (configPolymorphism != null) {
9495
// customization = mergeConfigAndAnnotationPolymorphism(configPolymorphism,

src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Optional;
1919

2020
import jakarta.json.bind.JsonbException;
21+
import jakarta.json.bind.config.PropertyNamingStrategy;
2122

2223
import org.eclipse.yasson.internal.model.JsonbCreator;
2324
import org.eclipse.yasson.internal.model.Property;
@@ -47,10 +48,11 @@ static boolean isSpecialAccessorMethod(Method method, Map<String, Property> clas
4748

4849
static JsonbCreator findCreator(Class<?> clazz,
4950
Constructor<?>[] declaredConstructors,
50-
AnnotationIntrospector introspector) {
51+
AnnotationIntrospector introspector,
52+
PropertyNamingStrategy propertyNamingStrategy) {
5153
if (clazz.isRecord()) {
5254
if (declaredConstructors.length == 1) {
53-
return introspector.createJsonbCreator(declaredConstructors[0], null, clazz);
55+
return introspector.createJsonbCreator(declaredConstructors[0], null, clazz, propertyNamingStrategy);
5456
}
5557
}
5658
return null;

src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTest.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -12,6 +12,7 @@
1212

1313
package org.eclipse.yasson.internal;
1414

15+
import jakarta.json.bind.config.PropertyNamingStrategy;
1516
import org.junit.jupiter.api.*;
1617
import static org.junit.jupiter.api.Assertions.*;
1718

@@ -40,6 +41,7 @@
4041
*/
4142
public class AnnotationIntrospectorTest {
4243
private final JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider());
44+
private final PropertyNamingStrategy propertyNamingStrategy = jsonbContext.getConfigProperties().getPropertyNamingStrategy();
4345

4446
/**
4547
* class under test.
@@ -48,29 +50,29 @@ public class AnnotationIntrospectorTest {
4850

4951
@Test
5052
public void testObjectShouldBeCreateableFromJsonbAnnotatedConstructor() {
51-
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class);
53+
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class, propertyNamingStrategy);
5254
assertParameters(ObjectWithJsonbCreatorAnnotatedConstructor.parameters(), creator);
5355
assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedConstructor.example(), creator);
5456
}
5557

5658
@Test
5759
public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethod() {
58-
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class);
60+
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class, propertyNamingStrategy);
5961
assertParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.parameters(), creator);
6062
assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.example(), creator);
6163
}
6264

6365
@Test
6466
public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethodIgnoringConstructorPorperties() {
65-
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.class);
67+
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.class, propertyNamingStrategy);
6668
assertParameters(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.parameters(), creator);
6769
assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.example(), creator);
6870
}
6971

7072
@Test
7173
public void testJsonbAnnotatedProtectedConstructorLeadsToAnException() {
7274
assertThrows(JsonbException.class, () -> {
73-
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedProtectedConstructor.class);
75+
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedProtectedConstructor.class, propertyNamingStrategy);
7476
assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedProtectedConstructor.example(), creator);
7577
});
7678
}
@@ -79,21 +81,21 @@ public void testJsonbAnnotatedProtectedConstructorLeadsToAnException() {
7981
@Disabled
8082
@Test
8183
public void testNoArgConstructorShouldBePreferredOverUnusableJsonbAnnotatedProtectedConstructor() {
82-
JsonbCreator creator = instrospector.getCreator(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.class);
84+
JsonbCreator creator = instrospector.getCreator(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.class, propertyNamingStrategy);
8385
assertParameters(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.parameters(), creator);
8486
assertCreatedInstanceContainsAllParameters(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.example(), creator);
8587
}
8688

8789
@Test
8890
public void testMoreThanOneAnnotatedCreatorMethodShouldLeadToAnException() {
8991
assertThrows(JsonbException.class,
90-
() -> instrospector.getCreator(ObjectWithTwoJsonbCreatorAnnotatedSpots.class),
92+
() -> instrospector.getCreator(ObjectWithTwoJsonbCreatorAnnotatedSpots.class, propertyNamingStrategy),
9193
() -> "More than one @" + JsonbCreator.class.getSimpleName());
9294
}
9395

9496
@Test
9597
public void testCreatorShouldBeNullOnMissingConstructorAnnotation() {
96-
assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class));
98+
assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class, propertyNamingStrategy));
9799
}
98100

99101
}

src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorWithoutOptionalModulesTest.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -12,19 +12,21 @@
1212

1313
package org.eclipse.yasson.internal;
1414

15-
import org.junit.jupiter.api.*;
16-
import static org.junit.jupiter.api.Assertions.*;
17-
1815
import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertCreatedInstanceContainsAllParameters;
1916
import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertParameters;
17+
import static org.junit.jupiter.api.Assertions.assertNull;
18+
import static org.junit.jupiter.api.Assertions.fail;
19+
20+
import jakarta.json.bind.JsonbConfig;
21+
import jakarta.json.bind.config.PropertyNamingStrategy;
22+
import jakarta.json.spi.JsonProvider;
2023

2124
import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedConstructor;
2225
import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedFactoryMethod;
2326
import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithoutAnnotatedConstructor;
2427
import org.eclipse.yasson.internal.model.JsonbCreator;
2528

26-
import jakarta.json.bind.JsonbConfig;
27-
import jakarta.json.spi.JsonProvider;
29+
import org.junit.jupiter.api.Test;
2830

2931
/**
3032
* Tests the {@link AnnotationIntrospector} with missing optional module "java.deskop", <br>
@@ -41,6 +43,7 @@ public class AnnotationIntrospectorWithoutOptionalModulesTest {
4143
* class under test.
4244
*/
4345
private static final AnnotationIntrospector instrospector = new AnnotationIntrospector(new JsonbContext(new JsonbConfig(), JsonProvider.provider()));
46+
private final PropertyNamingStrategy propertyNamingStrategy = propertyName -> propertyName;
4447

4548
@Test
4649
public void testNoConstructorPropertiesAnnotationWithoutOptionalModules() {
@@ -55,19 +58,19 @@ public void testNoConstructorPropertiesAnnotationWithoutOptionalModules() {
5558

5659
@Test
5760
public void testCreatorShouldBeNullOnMissingConstructorAnnotation() {
58-
assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class));
61+
assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class, propertyNamingStrategy));
5962
}
6063

6164
@Test
6265
public void testObjectShouldBeCreateableFromJsonbAnnotatedConstructorWithoutOptionalModules() {
63-
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class);
66+
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class, propertyNamingStrategy);
6467
assertParameters(ObjectWithJsonbCreatorAnnotatedConstructor.parameters(), creator);
6568
assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedConstructor.example(), creator);
6669
}
6770

6871
@Test
6972
public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethodWithoutOptionalModules() {
70-
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class);
73+
JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class, propertyNamingStrategy);
7174
assertParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.parameters(), creator);
7275
assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.example(), creator);
7376
}

src/test/java/org/eclipse/yasson/internal/ClassParserTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class ClassParserTest {
4040
public void testDefaultMappingFieldModifiers() {
4141
final JsonbAnnotatedElement<Class<?>> clsElement = introspector.collectAnnotations(FieldModifiersClass.class);
4242
ClassModel model = new ClassModel(FieldModifiersClass.class, introspector.introspectCustomization(clsElement,
43-
ClassCustomization.empty()), null, null);
43+
ClassCustomization.empty(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()), null, null);
4444
classParser.parseProperties(model, clsElement);
4545
assertTrue(model.getPropertyModel("finalString").isReadable());
4646
assertFalse(model.getPropertyModel("finalString").isWritable());
@@ -54,7 +54,7 @@ public void testDefaultMappingFieldModifiers() {
5454
public void testDefaultMappingMethodModifiers() {
5555
final JsonbAnnotatedElement<Class<?>> clsElement = introspector.collectAnnotations(MethodModifiersClass.class);
5656
ClassModel model = new ClassModel(FieldModifiersClass.class, introspector.introspectCustomization(clsElement,
57-
ClassCustomization.empty()), null, null);
57+
ClassCustomization.empty(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()), null, null);
5858
classParser.parseProperties(model, clsElement);
5959
assertFalse(model.getPropertyModel("publicFieldWithPrivateMethods").isReadable());
6060
assertFalse(model.getPropertyModel("publicFieldWithPrivateMethods").isWritable());
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
package org.eclipse.yasson.records;
13+
14+
import jakarta.json.bind.Jsonb;
15+
import jakarta.json.bind.JsonbBuilder;
16+
import jakarta.json.bind.JsonbConfig;
17+
import jakarta.json.bind.annotation.JsonbCreator;
18+
import jakarta.json.bind.config.PropertyNamingStrategy;
19+
import org.junit.jupiter.api.Test;
20+
21+
import static java.util.Objects.requireNonNull;
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
24+
public class CarWithCreateNamingStrategyTest {
25+
26+
// camel case is intentional for this test case
27+
public record Car(String brandName, String colorName) {
28+
29+
@JsonbCreator
30+
public Car {
31+
requireNonNull(brandName, "brandName");
32+
requireNonNull(colorName, "colorName");
33+
}
34+
}
35+
36+
@Test
37+
public void testRecordJsonbCreatorWithNamingStrategy() {
38+
// given
39+
final JsonbConfig config = new JsonbConfig()
40+
.withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES);
41+
final Jsonb jsonb = JsonbBuilder.create(config);
42+
43+
var json = """
44+
{
45+
"brand_name": "Volkswagen",
46+
"color_name": "Piano black"
47+
}
48+
""";
49+
50+
// when
51+
final Car car = jsonb.fromJson(json, Car.class);
52+
53+
// then
54+
assertEquals("Volkswagen", car.brandName());
55+
assertEquals("Piano black", car.colorName());
56+
}
57+
}

0 commit comments

Comments
 (0)