Skip to content

Commit

Permalink
Add support for JsonAlias annotation (#462)
Browse files Browse the repository at this point in the history
Copy JsonAlias annotations to the generated getter/setter methods.
  • Loading branch information
alicederyn committed Oct 9, 2022
2 parents 1ea0a69 + f04970a commit 3a38f58
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 12 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ guava=com.google.guava:guava:16.0
guavaTestlib=com.google.guava:guava-testlib:17.0
gwtUser=com.google.gwt:gwt-user:2.8.0
hamcrest=org.hamcrest:hamcrest-all:1.3
jacksonVersion=2.6.1
jacksonVersion=2.9.3
javassist=org.javassist:javassist:3.19.0-GA
jsr305=com.google.code.findbugs:jsr305:3.0.0
junit=junit:junit:4.12
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ private enum GenerateAnnotation {
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonProperty");
private static final String JACKSON_XML_ANNOTATION_PACKAGE =
"com.fasterxml.jackson.dataformat.xml.annotation";
private static final String JSON_ALIAS =
"com.fasterxml.jackson.annotation.JsonAlias";
private static final QualifiedName JSON_ANY_GETTER =
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonAnyGetter");
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonAnyGetter");
private static final QualifiedName JSON_ANY_SETTER =
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonAnySetter");
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonAnySetter");
/** Annotations which disable automatic generation of JsonProperty annotations. */
private static final Set<QualifiedName> DISABLE_PROPERTY_ANNOTATIONS = ImmutableSet.of(
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonIgnore"),
Expand All @@ -55,15 +57,15 @@ private JacksonSupport(Elements elements) {
public void addJacksonAnnotations(
Property.Builder resultBuilder,
ExecutableElement getterMethod) {
Optional<AnnotationMirror> jsonPropertyAnnotation = findAnnotationMirror(getterMethod,
JSON_PROPERTY);
Optional<AnnotationMirror> jsonPropertyAnnotation =
findAnnotationMirror(getterMethod, JSON_PROPERTY);
if (jsonPropertyAnnotation.isPresent()) {
resultBuilder.addAccessorAnnotations(Excerpts.add("%s%n", jsonPropertyAnnotation.get()));
} else {
switch (generateDefaultAnnotations(getterMethod)) {
case DEFAULT:
resultBuilder.addAccessorAnnotations(Excerpts.add(
"@%s(\"%s\")%n", JSON_PROPERTY, resultBuilder.getName()));
resultBuilder.addAccessorAnnotations(
Excerpts.add("@%s(\"%s\")%n", JSON_PROPERTY, resultBuilder.getName()));
break;
case JSON_ANY:
resultBuilder.addPutAnnotations(Excerpts.add("@%s%n", JSON_ANY_SETTER));
Expand All @@ -78,15 +80,18 @@ public void addJacksonAnnotations(
getterMethod
.getAnnotationMirrors()
.stream()
.filter(this::isXmlAnnotation)
.filter(this::shouldCopyAnnotation)
.forEach(annotation -> {
resultBuilder.addAccessorAnnotations(code -> code.addLine("%s", annotation));
resultBuilder.addAccessorAnnotations(Excerpts.add("%s%n", annotation));
});
}

private boolean isXmlAnnotation(AnnotationMirror mirror) {
Name pkg = elements.getPackageOf(mirror.getAnnotationType().asElement()).getQualifiedName();
return pkg.contentEquals(JACKSON_XML_ANNOTATION_PACKAGE);
private boolean shouldCopyAnnotation(AnnotationMirror mirror) {
TypeElement annotationTypeElement = (TypeElement) mirror.getAnnotationType().asElement();
Name qualifiedName = annotationTypeElement.getQualifiedName();
Name pkg = elements.getPackageOf(annotationTypeElement).getQualifiedName();
return pkg.contentEquals(JACKSON_XML_ANNOTATION_PACKAGE)
|| qualifiedName.contentEquals(JSON_ALIAS);
}

private static GenerateAnnotation generateDefaultAnnotations(ExecutableElement getterMethod) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.inferred.freebuilder.processor;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import org.inferred.freebuilder.FreeBuilder;
import org.inferred.freebuilder.processor.source.SourceBuilder;
import org.inferred.freebuilder.processor.source.feature.FeatureSet;
import org.inferred.freebuilder.processor.source.feature.StaticFeatureSet;
import org.inferred.freebuilder.processor.source.testing.BehaviorTester;
import org.inferred.freebuilder.processor.source.testing.ParameterizedBehaviorTestFactory.Shared;
import org.inferred.freebuilder.processor.source.testing.TestBuilder;
import org.junit.Test;

public class JacksonIntegrationTest {

@Shared public BehaviorTester behaviorTester;

@Test
public void testJsonAliasSupport() {
FeatureSet featureSet = new StaticFeatureSet();
BehaviorTester.create(featureSet)
.with(new Processor(featureSet))
.with(SourceBuilder.forTesting()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("@%s(builder = DataType.Builder.class)", JsonDeserialize.class)
.addLine("public abstract class DataType {")
.addLine(" @%s({\"a\", \"theagame\"})", JsonAlias.class)
.addLine(" public abstract int propertyA();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}"))
.with(testBuilder()
.addLine("DataType expected = new DataType.Builder().propertyA(13).build();")
.addLine("%1$s mapper = new %1$s();", ObjectMapper.class)
.addLine("String canonicalJson = \"{ \\\"propertyA\\\": 13 }\";")
.addLine("DataType canonical = mapper.readValue(canonicalJson, DataType.class);")
.addLine("assertEquals(expected, canonical);")
.addLine("String alternative1Json = \"{ \\\"a\\\": 13 }\";")
.addLine("DataType alternative1 = mapper.readValue(canonicalJson, DataType.class);")
.addLine("assertEquals(expected, alternative1);")
.addLine("String alternative2Json = \"{ \\\"theagame\\\": 13 }\";")
.addLine("DataType alternative2 = mapper.readValue(canonicalJson, DataType.class);")
.addLine("assertEquals(expected, alternative2);")
.build())
.runTest();
}

private static TestBuilder testBuilder() {
return new TestBuilder().addImport("com.example.DataType");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
Expand Down Expand Up @@ -145,6 +146,21 @@ public void jsonAnyGetterAnnotationDisablesImplicitProperty() throws CannotGener
assertThat(property.getAccessorAnnotations()).named("property accessor annotations").isEmpty();
}

@Test
public void jsonAliasAnnotationCopied() throws CannotGenerateCodeException {
GeneratedBuilder builder = (GeneratedBuilder) analyser.analyse(model.newType(
"package com.example;",
"@" + JsonDeserialize.class.getName() + "(builder = DataType.Builder.class)",
"public interface DataType {",
" @" + JsonAlias.class.getName() + "({\"foo\", \"bar\"}) int getFooBar();",
" class Builder extends DataType_Builder {}",
"}"));

Property property = getOnlyElement(builder.getGeneratorsByProperty().keySet());
assertPropertyHasAnnotation(property, JsonProperty.class, "@JsonProperty(\"fooBar\")");
assertPropertyHasAnnotation(property, JsonAlias.class, "@JsonAlias({\"foo\", \"bar\"})");
}

private static void assertPropertyHasAnnotation(
Property property, Class<? extends Annotation> annotationClass, String annotationString) {
Excerpt annotationExcerpt = property.getAccessorAnnotations()
Expand Down

0 comments on commit 3a38f58

Please sign in to comment.