[FEATURE] Support Jackson3 in @Jacksonized #3950

This commit is contained in:
Lars Uffmann committed 2025-09-21 11:54:28 +02:00
1 parent 4114669bc1
commit 086b351830
10 files changed
+95 -22

No files matched your search

@@ -467,6 +467,7 @@ public class HandlerUtil {
"com.fasterxml.jackson.annotation.JsonUnwrapped",
"com.fasterxml.jackson.annotation.JsonView",
"com.fasterxml.jackson.databind.annotation.JsonDeserialize",
"tools.jackson.databind.annotation.JsonDeserialize",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText",
@@ -488,6 +489,7 @@ public class HandlerUtil {
"com.fasterxml.jackson.annotation.JsonTypeName",
"com.fasterxml.jackson.annotation.JsonView",
"com.fasterxml.jackson.databind.annotation.JsonNaming",
"tools.jackson.databind.annotation.JsonNaming",
}));
}
@@ -62,8 +62,10 @@ import lombok.spi.Provides;
@HandlerPriority(-512) // Above Handle(Super)Builder's level (builders must be already generated).
public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
private static final char[][] JSON_POJO_BUILDER_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder");
private static final char[][] JSON_DESERIALIZE_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonDeserialize");
private static final char[][] JACKSON3_JSON_POJO_BUILDER_ANNOTATION = Eclipse.fromQualifiedName("tools.jackson.databind.annotation.JsonPOJOBuilder");
private static final char[][] JACKSON2_JSON_POJO_BUILDER_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder");
private static final char[][] JACKSON3_JSON_DESERIALIZE_ANNOTATION = Eclipse.fromQualifiedName("tools.jackson.databind.annotation.JsonDeserialize");
private static final char[][] JACKSON2_JSON_DESERIALIZE_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonDeserialize");
private static final char[][] JSON_PROPERTY_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.annotation.JsonProperty");
@Override public void handle(AnnotationValues<Jacksonized> annotation, Annotation ast, EclipseNode annotationNode) {
@@ -136,12 +138,20 @@ public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}
if (hasAnnotation("tools.jackson.databind.annotation.JsonDeserialize", tdNode)) {
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
TypeReference builderClassExpression = namePlusTypeParamsToTypeReference(builderClassNode, null, p);
ClassLiteralAccess builderClassLiteralAccess = new ClassLiteralAccess(td.sourceEnd, builderClassExpression);
MemberValuePair builderMvp = new MemberValuePair("builder".toCharArray(), td.sourceStart, td.sourceEnd, builderClassLiteralAccess);
td.annotations = addAnnotation(td, td.annotations, JSON_DESERIALIZE_ANNOTATION, builderMvp);
/* if (jackson2) */ {
td.annotations = addAnnotation(td, td.annotations, JACKSON2_JSON_DESERIALIZE_ANNOTATION, builderMvp);
}
/* if (jackson3) */ {
td.annotations = addAnnotation(td, td.annotations, JACKSON3_JSON_DESERIALIZE_ANNOTATION, builderMvp);
}
// Copy annotations from the class to the builder class.
Annotation[] copyableAnnotations = findJacksonAnnotationsOnClass(td, tdNode);
builderClass.annotations = copyAnnotations(builderClass, builderClass.annotations, copyableAnnotations);
@@ -151,8 +161,12 @@ public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
MemberValuePair withPrefixMvp = new MemberValuePair("withPrefix".toCharArray(), builderClass.sourceStart, builderClass.sourceEnd, withPrefixLiteral);
StringLiteral buildMethodNameLiteral = new StringLiteral(buildMethodName.toCharArray(), builderClass.sourceStart, builderClass.sourceEnd, 0);
MemberValuePair buildMethodNameMvp = new MemberValuePair("buildMethodName".toCharArray(), builderClass.sourceStart, builderClass.sourceEnd, buildMethodNameLiteral);
builderClass.annotations = addAnnotation(builderClass, builderClass.annotations, JSON_POJO_BUILDER_ANNOTATION, withPrefixMvp, buildMethodNameMvp);
/* if (jackson2) */ {
builderClass.annotations = addAnnotation(builderClass, builderClass.annotations, JACKSON2_JSON_POJO_BUILDER_ANNOTATION, withPrefixMvp, buildMethodNameMvp);
}
/* if (jackson3) */ {
builderClass.annotations = addAnnotation(builderClass, builderClass.annotations, JACKSON3_JSON_POJO_BUILDER_ANNOTATION, withPrefixMvp, buildMethodNameMvp);
}
// @SuperBuilder? Make it package-private!
if (superBuilderAnnotationNode != null)
builderClass.modifiers = builderClass.modifiers & ~ClassFileConstants.AccPrivate;
@@ -168,13 +168,28 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}
JCExpression jsonDeserializeType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonDeserialize");
JCExpression builderClassExpression = namePlusTypeParamsToTypeReference(maker, tdNode, annotationNode.toName(builderClassName), false, List.<JCTypeParameter>nil());
JCFieldAccess builderClassReference = maker.Select(builderClassExpression, annotatedNode.toName("class"));
JCExpression assign = maker.Assign(maker.Ident(annotationNode.toName("builder")), builderClassReference);
JCAnnotation annotationJsonDeserialize = maker.Annotation(jsonDeserializeType, List.of(assign));
recursiveSetGeneratedBy(annotationJsonDeserialize, annotationNode);
td.mods.annotations = td.mods.annotations.append(annotationJsonDeserialize);
if (hasAnnotation("tools.jackson.databind.annotation.JsonDeserialize", tdNode)) {
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}
/* if (jackson2) */ {
JCExpression jsonDeserializeType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonDeserialize");
JCExpression builderClassExpression = namePlusTypeParamsToTypeReference(maker, tdNode, annotationNode.toName(builderClassName), false, List.<JCTypeParameter>nil());
JCFieldAccess builderClassReference = maker.Select(builderClassExpression, annotatedNode.toName("class"));
JCExpression assign = maker.Assign(maker.Ident(annotationNode.toName("builder")), builderClassReference);
JCAnnotation annotationJsonDeserialize = maker.Annotation(jsonDeserializeType, List.of(assign));
recursiveSetGeneratedBy(annotationJsonDeserialize, annotationNode);
td.mods.annotations = td.mods.annotations.append(annotationJsonDeserialize);
}
/* if (jackson3) */ {
JCExpression jsonDeserializeType = chainDots(annotatedNode, "tools", "jackson", "databind", "annotation", "JsonDeserialize");
JCExpression builderClassExpression = namePlusTypeParamsToTypeReference(maker, tdNode, annotationNode.toName(builderClassName), false, List.<JCTypeParameter>nil());
JCFieldAccess builderClassReference = maker.Select(builderClassExpression, annotatedNode.toName("class"));
JCExpression assign = maker.Assign(maker.Ident(annotationNode.toName("builder")), builderClassReference);
JCAnnotation annotationJsonDeserialize = maker.Annotation(jsonDeserializeType, List.of(assign));
recursiveSetGeneratedBy(annotationJsonDeserialize, annotationNode);
td.mods.annotations = td.mods.annotations.append(annotationJsonDeserialize);
}
// Copy annotations from the class to the builder class.
List<JCAnnotation> copyableAnnotations = findJacksonAnnotationsOnClass(tdNode);
@@ -183,15 +198,25 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
recursiveSetGeneratedBy(anno, annotationNode);
}
builderClass.mods.annotations = builderClass.mods.annotations.appendList(copiedAnnotations);
// Insert @JsonPOJOBuilder on the builder class.
JCExpression jsonPOJOBuilderType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonPOJOBuilder");
JCExpression withPrefixExpr = maker.Assign(maker.Ident(annotationNode.toName("withPrefix")), maker.Literal(setPrefix));
JCExpression buildMethodNameExpr = maker.Assign(maker.Ident(annotationNode.toName("buildMethodName")), maker.Literal(buildMethodName));
JCAnnotation annotationJsonPOJOBuilder = maker.Annotation(jsonPOJOBuilderType, List.of(withPrefixExpr, buildMethodNameExpr));
recursiveSetGeneratedBy(annotationJsonPOJOBuilder, annotatedNode);
builderClass.mods.annotations = builderClass.mods.annotations.append(annotationJsonPOJOBuilder);
/* if (jackson2) */ {
// Insert @JsonPOJOBuilder on the builder class.
JCExpression jsonPOJOBuilderType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonPOJOBuilder");
JCExpression withPrefixExpr = maker.Assign(maker.Ident(annotationNode.toName("withPrefix")), maker.Literal(setPrefix));
JCExpression buildMethodNameExpr = maker.Assign(maker.Ident(annotationNode.toName("buildMethodName")), maker.Literal(buildMethodName));
JCAnnotation annotationJsonPOJOBuilder = maker.Annotation(jsonPOJOBuilderType, List.of(withPrefixExpr, buildMethodNameExpr));
recursiveSetGeneratedBy(annotationJsonPOJOBuilder, annotatedNode);
builderClass.mods.annotations = builderClass.mods.annotations.append(annotationJsonPOJOBuilder);
}
/* if (jackson3) */ {
// Insert @JsonPOJOBuilder on the builder class.
JCExpression jsonPOJOBuilderType = chainDots(annotatedNode, "tools", "jackson", "databind", "annotation", "JsonPOJOBuilder");
JCExpression withPrefixExpr = maker.Assign(maker.Ident(annotationNode.toName("withPrefix")), maker.Literal(setPrefix));
JCExpression buildMethodNameExpr = maker.Assign(maker.Ident(annotationNode.toName("buildMethodName")), maker.Literal(buildMethodName));
JCAnnotation annotationJsonPOJOBuilder = maker.Annotation(jsonPOJOBuilderType, List.of(withPrefixExpr, buildMethodNameExpr));
recursiveSetGeneratedBy(annotationJsonPOJOBuilder, annotatedNode);
builderClass.mods.annotations = builderClass.mods.annotations.append(annotationJsonPOJOBuilder);
}
// @SuperBuilder? Make it package-private!
if (superBuilderAnnotationNode != null) builderClass.mods.flags = builderClass.mods.flags & ~Flags.PRIVATE;
}
@@ -0,0 +1,12 @@
package tools.jackson.databind.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonDeserialize {
public Class<?> builder() default Void.class;
}
@@ -0,0 +1,10 @@
package tools.jackson.databind.annotation;
import java.lang.annotation.*;
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonPOJOBuilder {
public String buildMethodName() default "build";
public String withPrefix() default "with";
}
@@ -2,12 +2,14 @@
//CONF: lombok.builder.className = Test*Name
import java.util.List;
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderComplex.TestVoidName.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderComplex.TestVoidName.class)
class JacksonizedBuilderComplex {
private static <T extends Number> void testVoidWithGenerics(T number, int arg2, String arg3, JacksonizedBuilderComplex selfRef) {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "with", buildMethodName = "execute")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "with", buildMethodName = "execute")
public static class TestVoidName<T extends Number> {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@@ -3,6 +3,7 @@ import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSimple.JacksonizedBuilderSimpleBuilder.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSimple.JacksonizedBuilderSimpleBuilder.class)
class JacksonizedBuilderSimple<T> {
private final int noshow = 0;
private final int yes;
@@ -18,6 +19,7 @@ class JacksonizedBuilderSimple<T> {
@lombok.Generated
@JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
protected static class JacksonizedBuilderSimpleBuilder<T> {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder.class)
public class JacksonizedBuilderSingular {
@JsonAnySetter
private Map<String, Object> any;
@@ -26,6 +27,7 @@ public class JacksonizedBuilderSingular {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
public static class JacksonizedBuilderSingularBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@@ -6,11 +6,13 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedOnRecord.JacksonizedOnRecordBuilder.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedOnRecord.JacksonizedOnRecordBuilder.class)
public record JacksonizedOnRecord(@JsonProperty("test") @Nullable String string, @JsonAnySetter List<String> values) {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@JsonIgnoreProperties
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
public static class JacksonizedOnRecordBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@@ -2,6 +2,7 @@
public class JacksonizedSuperBuilderSimple {
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedSuperBuilderSimple.Parent.ParentBuilderImpl.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedSuperBuilderSimple.Parent.ParentBuilderImpl.class)
public static class Parent {
int field1;
@java.lang.SuppressWarnings("all")
@@ -36,6 +37,7 @@ public class JacksonizedSuperBuilderSimple {
@lombok.Generated
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
static final class ParentBuilderImpl extends JacksonizedSuperBuilderSimple.Parent.ParentBuilder<JacksonizedSuperBuilderSimple.Parent, JacksonizedSuperBuilderSimple.Parent.ParentBuilderImpl> {
@java.lang.SuppressWarnings("all")
@lombok.Generated