diff --git a/gradle.properties b/gradle.properties index 544b304d..00f5a4b9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/src/main/java/org/inferred/freebuilder/processor/JacksonSupport.java b/src/main/java/org/inferred/freebuilder/processor/JacksonSupport.java index f2ad62eb..1ea2b90f 100644 --- a/src/main/java/org/inferred/freebuilder/processor/JacksonSupport.java +++ b/src/main/java/org/inferred/freebuilder/processor/JacksonSupport.java @@ -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 DISABLE_PROPERTY_ANNOTATIONS = ImmutableSet.of( QualifiedName.of("com.fasterxml.jackson.annotation", "JsonIgnore"), @@ -55,15 +57,15 @@ private JacksonSupport(Elements elements) { public void addJacksonAnnotations( Property.Builder resultBuilder, ExecutableElement getterMethod) { - Optional jsonPropertyAnnotation = findAnnotationMirror(getterMethod, - JSON_PROPERTY); + Optional 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)); @@ -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) { diff --git a/src/test/java/org/inferred/freebuilder/processor/JacksonIntegrationTest.java b/src/test/java/org/inferred/freebuilder/processor/JacksonIntegrationTest.java new file mode 100644 index 00000000..639cf42a --- /dev/null +++ b/src/test/java/org/inferred/freebuilder/processor/JacksonIntegrationTest.java @@ -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"); + } +} diff --git a/src/test/java/org/inferred/freebuilder/processor/JacksonSupportTest.java b/src/test/java/org/inferred/freebuilder/processor/JacksonSupportTest.java index c39baecb..3c3fce04 100644 --- a/src/test/java/org/inferred/freebuilder/processor/JacksonSupportTest.java +++ b/src/test/java/org/inferred/freebuilder/processor/JacksonSupportTest.java @@ -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; @@ -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 annotationClass, String annotationString) { Excerpt annotationExcerpt = property.getAccessorAnnotations()