Skip to content

Commit 83b9c46

Browse files
committed
Add support to configure adding jackson @JsonPOJOBuilder annotations to generated builders
1 parent c4a6113 commit 83b9c46

7 files changed

Lines changed: 147 additions & 0 deletions

File tree

options.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ The names used for generated methods, classes, etc. can be changed via the follo
5454
| `@RecordBuilder.Options(fileIndent = " ")` | Return the file indent to use. |
5555
| `@RecordBuilder.Options(prefixEnclosingClassNames = true/false)` | If the record is declared inside another class, the outer class's name will be prefixed to the builder name if this returns true. The default is `true`. |
5656

57+
## Jackson Support
58+
59+
| option | details |
60+
|--------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
61+
| `@RecordBuilder.Options(addJacksonAnnotations = true/false)` | If true, builders will be annotated with `@JsonPOJOBuilder` Jackson annotations which can be used in combination with `@JsonDeserialize(builder = ...)`. See [TestJacksonAnnotations](./record-builder-test/src/test/java/io/soabase/recordbuilder/test/TestJacksonAnnotations.java) for an example. The default is `false`. |
62+
5763
## Miscellaneous
5864

5965
| option | details |

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<hibernate-validator-version>6.2.0.Final</hibernate-validator-version>
6565
<jakarta-validation-api-version>3.1.0</jakarta-validation-api-version>
6666
<javax-el-version>3.0.1-b09</javax-el-version>
67+
<jackson-version>2.19.0</jackson-version>
6768
<central-publishing-maven-plugin-version>0.7.0</central-publishing-maven-plugin-version>
6869
</properties>
6970

@@ -165,6 +166,12 @@
165166
<version>${hibernate-validator-version}</version>
166167
</dependency>
167168

169+
<dependency>
170+
<groupId>com.fasterxml.jackson.core</groupId>
171+
<artifactId>jackson-databind</artifactId>
172+
<version>${jackson-version}</version>
173+
</dependency>
174+
168175
<dependency>
169176
<groupId>org.glassfish</groupId>
170177
<artifactId>javax.el</artifactId>

record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@
353353
* @see #nullablePattern
354354
*/
355355
boolean defaultNotNull() default false;
356+
357+
boolean addJacksonAnnotations() default false;
356358
}
357359

358360
@Retention(RetentionPolicy.CLASS)

record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordBuilderProcessor.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class InternalRecordBuilderProcessor {
8686
builder.addAnnotation(recordBuilderGeneratedAnnotation);
8787
}
8888

89+
addJacksonAnnotations();
90+
8991
if (!validateMethodNameConflicts(processingEnv, recordFacade.element())) {
9092
builderType = Optional.empty();
9193
return;
@@ -196,6 +198,18 @@ private void addVisibility(boolean builderIsInRecordPackage, Set<Modifier> modif
196198
}
197199
}
198200

201+
private void addJacksonAnnotations() {
202+
if (!metaData.addJacksonAnnotations()) {
203+
return;
204+
}
205+
206+
final var annotationSpec = AnnotationSpec
207+
.builder(ClassName.get("com.fasterxml.jackson.databind.annotation", "JsonPOJOBuilder"))
208+
.addMember("withPrefix", "$S", metaData.setterPrefix()).build();
209+
210+
builder.addAnnotation(annotationSpec);
211+
}
212+
199213
private void addOnceOnlySupport() {
200214
if (recordComponents.isEmpty()) {
201215
return;

record-builder-test/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@
6060
<scope>provided</scope>
6161
</dependency>
6262

63+
<dependency>
64+
<groupId>com.fasterxml.jackson.core</groupId>
65+
<artifactId>jackson-databind</artifactId>
66+
</dependency>
67+
6368
<dependency>
6469
<groupId>org.glassfish</groupId>
6570
<artifactId>javax.el</artifactId>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2019 The original author or authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.soabase.recordbuilder.test;
17+
18+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
19+
import io.soabase.recordbuilder.core.RecordBuilder;
20+
21+
import java.util.Map;
22+
23+
public interface JacksonAnnotated {
24+
String name();
25+
26+
String type();
27+
28+
Map<String, Object> properties();
29+
30+
@RecordBuilder
31+
@RecordBuilder.Options(addJacksonAnnotations = true, useImmutableCollections = true, prefixEnclosingClassNames = false)
32+
@JsonDeserialize(builder = JacksonAnnotatedRecordBuilder.class)
33+
record JacksonAnnotatedRecord(String name, @RecordBuilder.Initializer("DEFAULT_TYPE") String type,
34+
Map<String, Object> properties) implements JacksonAnnotated {
35+
public static final String DEFAULT_TYPE = "dummy";
36+
}
37+
38+
@RecordBuilder
39+
@RecordBuilder.Options(addJacksonAnnotations = true, useImmutableCollections = true, prefixEnclosingClassNames = false, setterPrefix = "set")
40+
@JsonDeserialize(builder = JacksonAnnotatedRecordCustomSetterPrefixBuilder.class)
41+
record JacksonAnnotatedRecordCustomSetterPrefix(String name, @RecordBuilder.Initializer("DEFAULT_TYPE") String type,
42+
Map<String, Object> properties) implements JacksonAnnotated {
43+
public static final String DEFAULT_TYPE = "dummy";
44+
}
45+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2019 The original author or authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.soabase.recordbuilder.test;
17+
18+
import com.fasterxml.jackson.core.JsonProcessingException;
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
21+
import io.soabase.recordbuilder.test.JacksonAnnotated.JacksonAnnotatedRecord;
22+
import io.soabase.recordbuilder.test.JacksonAnnotated.JacksonAnnotatedRecordCustomSetterPrefix;
23+
import org.assertj.core.api.InstanceOfAssertFactories;
24+
import org.junit.jupiter.params.ParameterizedTest;
25+
import org.junit.jupiter.params.provider.Arguments;
26+
import org.junit.jupiter.params.provider.MethodSource;
27+
import org.junit.jupiter.params.provider.ValueSource;
28+
29+
import java.util.Arrays;
30+
import java.util.stream.Stream;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.junit.jupiter.params.provider.Arguments.arguments;
34+
35+
class TestJacksonAnnotations {
36+
private final ObjectMapper objectMapper = new ObjectMapper();
37+
38+
@ParameterizedTest
39+
@MethodSource("recordBuilders")
40+
void addsJsonPOJOBuilderAnnotation(Class<? extends JacksonAnnotated> type, String expectedPrefix) {
41+
final var annotations = Arrays.stream(type.getAnnotations()).toList();
42+
assertThat(annotations).filteredOn(annotation -> annotation.annotationType().equals(JsonPOJOBuilder.class))
43+
.hasSize(1).first().asInstanceOf(InstanceOfAssertFactories.type(JsonPOJOBuilder.class))
44+
.satisfies(annotation -> {
45+
assertThat(annotation.withPrefix()).isEqualTo(expectedPrefix);
46+
});
47+
}
48+
49+
static Stream<Arguments> recordBuilders() {
50+
return Stream.of(arguments(JacksonAnnotatedRecordBuilder.class, ""),
51+
arguments(JacksonAnnotatedRecordCustomSetterPrefixBuilder.class, "set"));
52+
}
53+
54+
@ParameterizedTest
55+
@ValueSource(classes = { JacksonAnnotatedRecord.class, JacksonAnnotatedRecordCustomSetterPrefix.class })
56+
void deserializingModelInvokesBuilder(Class<? extends JacksonAnnotated> type) throws JsonProcessingException {
57+
final var json = """
58+
{
59+
"name" : "test"
60+
}
61+
""";
62+
63+
final var model = objectMapper.readValue(json, type);
64+
assertThat(model.name()).isEqualTo("test");
65+
assertThat(model.type()).isEqualTo("dummy"); // default value
66+
assertThat(model.properties()).isNotNull().isEmpty(); // non-null initialized immutable collection
67+
}
68+
}

0 commit comments

Comments
 (0)