Add support to configure adding jackson @JsonPOJOBuilder annotations to generated builders#232
Add support to configure adding jackson @JsonPOJOBuilder annotations to generated builders#232maff wants to merge 2 commits intoRandgalt:masterfrom
Conversation
|
@Randgalt any feedback on this approach? I would be great to have this as an option for projects relying on Jackson and still wanting to benefit from RecordBuilder's initialization & defaults logic. |
| return; | ||
| } | ||
|
|
||
| final var annotationSpec = AnnotationSpec |
There was a problem hiding this comment.
I'm afraid it's not that simple. Recently, Jackson 3 was released with changed package names. Is it possible to check what annotations are presented on the classpath and add appropriate annotation here?
…to generated builders
83b9c46 to
227ce69
Compare
Introduces a dedicated Jackson option structure. JSONPojoBuilder support can be enabled with a boolean flag. Detects available jackson version on the classpath and adds the respective annotations when enabled. Jackson version can be set to AUTO (adds annotation for every found version) or to JACKSON_2 or JACKSON_3 (only adds annotations for defined version, fails the build when library is not on the classpath).
227ce69 to
408c108
Compare
|
@Randgalt @semyon-levin-workato Updated to support both Jackson 2 and 3 by detecting available classes on the classpath, with support to specify a specific version in case both versions are present. Would be awesome if we could progress with this feature for better out-of-the-box Jackson compatibility. |
|
@Randgalt could you please look into this? |
| case AUTO -> { | ||
| if (!jackson2Present && !jackson3Present) { | ||
| processingEnv.getMessager().printMessage(ERROR, | ||
| "jackson.jsonPOJOBuilder is enabled but Jackson is not found on classpath. " | ||
| + "Add jackson-databind dependency or disable jsonPOJOBuilder."); | ||
| return; | ||
| } | ||
|
|
||
| if (jackson2Present) { | ||
| addJacksonAnnotations(metaData, builder, JACKSON_2_ANNOTATION_PACKAGE); | ||
| } | ||
|
|
||
| if (jackson3Present) { | ||
| addJacksonAnnotations(metaData, builder, JACKSON_3_ANNOTATION_PACKAGE); | ||
| } | ||
| } | ||
|
|
||
| case JACKSON_2 -> { | ||
| if (!jackson2Present) { | ||
| processingEnv.getMessager().printMessage(ERROR, | ||
| "jackson.version is set to JACKSON_2 but Jackson 2.x is not found on classpath. " | ||
| + "Add jackson-databind 2.x dependency or change version to AUTO."); | ||
| return; | ||
| } | ||
|
|
||
| addJacksonAnnotations(metaData, builder, JACKSON_2_ANNOTATION_PACKAGE); | ||
| } | ||
|
|
||
| case JACKSON_3 -> { | ||
| if (!jackson3Present) { | ||
| processingEnv.getMessager().printMessage(ERROR, | ||
| "jackson.version is set to JACKSON_3 but Jackson 3.x is not found on classpath. " | ||
| + "Add jackson-databind 3.x dependency or change version to AUTO."); | ||
| return; | ||
| } | ||
|
|
||
| addJacksonAnnotations(metaData, builder, JACKSON_3_ANNOTATION_PACKAGE); | ||
| } |
There was a problem hiding this comment.
Let's remove those returns
| case AUTO -> { | |
| if (!jackson2Present && !jackson3Present) { | |
| processingEnv.getMessager().printMessage(ERROR, | |
| "jackson.jsonPOJOBuilder is enabled but Jackson is not found on classpath. " | |
| + "Add jackson-databind dependency or disable jsonPOJOBuilder."); | |
| return; | |
| } | |
| if (jackson2Present) { | |
| addJacksonAnnotations(metaData, builder, JACKSON_2_ANNOTATION_PACKAGE); | |
| } | |
| if (jackson3Present) { | |
| addJacksonAnnotations(metaData, builder, JACKSON_3_ANNOTATION_PACKAGE); | |
| } | |
| } | |
| case JACKSON_2 -> { | |
| if (!jackson2Present) { | |
| processingEnv.getMessager().printMessage(ERROR, | |
| "jackson.version is set to JACKSON_2 but Jackson 2.x is not found on classpath. " | |
| + "Add jackson-databind 2.x dependency or change version to AUTO."); | |
| return; | |
| } | |
| addJacksonAnnotations(metaData, builder, JACKSON_2_ANNOTATION_PACKAGE); | |
| } | |
| case JACKSON_3 -> { | |
| if (!jackson3Present) { | |
| processingEnv.getMessager().printMessage(ERROR, | |
| "jackson.version is set to JACKSON_3 but Jackson 3.x is not found on classpath. " | |
| + "Add jackson-databind 3.x dependency or change version to AUTO."); | |
| return; | |
| } | |
| addJacksonAnnotations(metaData, builder, JACKSON_3_ANNOTATION_PACKAGE); | |
| } | |
| case AUTO -> { | |
| if (!jackson2Present && !jackson3Present) { | |
| processingEnv.getMessager().printMessage(ERROR, | |
| "jackson.jsonPOJOBuilder is enabled but Jackson is not found on classpath. " | |
| + "Add jackson-databind dependency or disable jsonPOJOBuilder."); | |
| } | |
| else if (jackson2Present) { | |
| addJacksonAnnotations(metaData, builder, JACKSON_2_ANNOTATION_PACKAGE); | |
| } | |
| else /*if (jackson3Present)*/ { | |
| addJacksonAnnotations(metaData, builder, JACKSON_3_ANNOTATION_PACKAGE); | |
| } | |
| } | |
| case JACKSON_2 -> { | |
| if (!jackson2Present) { | |
| processingEnv.getMessager().printMessage(ERROR, | |
| "jackson.version is set to JACKSON_2 but Jackson 2.x is not found on classpath. " | |
| + "Add jackson-databind 2.x dependency or change version to AUTO."); | |
| } | |
| else { | |
| addJacksonAnnotations(metaData, builder, JACKSON_2_ANNOTATION_PACKAGE); | |
| } | |
| } | |
| case JACKSON_3 -> { | |
| if (!jackson3Present) { | |
| processingEnv.getMessager().printMessage(ERROR, | |
| "jackson.version is set to JACKSON_3 but Jackson 3.x is not found on classpath. " | |
| + "Add jackson-databind 3.x dependency or change version to AUTO."); | |
| } else { | |
| addJacksonAnnotations(metaData, builder, JACKSON_3_ANNOTATION_PACKAGE); | |
| } | |
| } |
| <version>${jackson-version}</version> | ||
| <version>${jackson2-version}</version> | ||
| </dependency> | ||
| <dependency> |
| * Specifies which Jackson version(s) to use when generating builder annotations. | ||
| */ | ||
| enum JacksonVersion { | ||
| /** |
There was a problem hiding this comment.
Let's add a NONE attribute here and remove @JacksonConfig. I'd also rename this RecordBuilderJacksonVersion or something to avoid name collisions with other projects (I realize some other names in here suffer from this).
| */ | ||
| @Retention(RetentionPolicy.CLASS) | ||
| @Target(ElementType.ANNOTATION_TYPE) | ||
| @interface JacksonConfig { |
There was a problem hiding this comment.
See comment below. We can remove this annotation.
| * | ||
| * @see JacksonConfig | ||
| */ | ||
| JacksonConfig jackson() default @JacksonConfig; |
There was a problem hiding this comment.
| JacksonConfig jackson() default @JacksonConfig; | |
| JacksonVersion jackson() default JacksonVersion.NONE; |
| jackson3Present = isAnnotationClassPresent(JACKSON_3_ANNOTATION_PACKAGE, JSON_POJO_BUILDER); | ||
| } | ||
|
|
||
| private boolean isAnnotationClassPresent(String packageName, String className) { |
There was a problem hiding this comment.
className is always JSON_POJO_BUILDER
| private final boolean jackson2Present; | ||
| private final boolean jackson3Present; | ||
|
|
||
| JacksonSupport(ProcessingEnvironment processingEnv) { |
There was a problem hiding this comment.
Given how this is used it would be better as a utility class with a private ctor and the methods becoming static.
| return processingEnv.getElementUtils().getTypeElement(packageName + "." + className) != null; | ||
| } | ||
|
|
||
| public void addJacksonAnnotations(RecordBuilder.Options metaData, TypeSpec.Builder builder) { |
| if (!anyJacksonAnnotationEnabled(metaData)) { | ||
| return; | ||
| } |
|
|
||
| private void addJsonPOJOBuilderAnnotation(RecordBuilder.Options metaData, TypeSpec.Builder builder, | ||
| String packageName) { | ||
| final var annotationSpec = AnnotationSpec.builder(ClassName.get(packageName, JSON_POJO_BUILDER)) |
There was a problem hiding this comment.
We also need to consider metaData.builderMethodName() and metaData.withClassMethodPrefix() right? JsonPOJOBuilder has attributes for that.
|
My apologies for how long this has taken but I just haven't had a lot of time recently. I've done a first pass review. In general I'm very positive on this. Thank you. |
Adds a new option structure to instruct record-builder to add a Jackson
@JsonPOJOBuilderannotation to the generated builder. While this implements library-specifics, it does not add Jackson as dependency (only on the test module).Example usage:
Auto-detect Jackson version (default)
Explicitly add Jackson 2.x annotation (fail if J2 is not available):
With custom setter prefix:
Closes #229