Skip to content
This repository has been archived by the owner on Oct 16, 2024. It is now read-only.

Commit

Permalink
#417 Support @JsonAnyGetter
Browse files Browse the repository at this point in the history
  • Loading branch information
alicederyn authored Dec 11, 2019
2 parents adec063 + ba8c441 commit 197d43f
Show file tree
Hide file tree
Showing 9 changed files with 455 additions and 35 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ configurations {
dependencies {
vanillaCompile guava
vanillaCompile gwtUser
vanillaCompile jackson('core')
vanillaCompile jackson('databind')
vanillaTestCompile junit
}
task vanillaTest {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.inferred.freebuilder;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.util.Map;

@FreeBuilder
@JsonDeserialize(builder = JacksonAnyGetterType.Builder.class)
public interface JacksonAnyGetterType {
@JsonProperty("simple_property")
String getSimpleProperty();
@JsonAnyGetter
Map<String, JsonNode> getUnknownProperties();

class Builder extends JacksonAnyGetterType_Builder {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.inferred.freebuilder;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.TextNode;
import org.junit.Test;

import java.io.IOException;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class JacksonAnyGetterTypeTest {

private final ObjectMapper objectMapper = new ObjectMapper();

@Test
public void testDeserializeJsonWithAnyGetter() throws IOException {
JacksonAnyGetterType parsed = objectMapper.readValue(
"{\"simple_property\": \"simpleValue\", \"other_property\": 3}",
JacksonAnyGetterType.class);

assertEquals("simpleValue", parsed.getSimpleProperty());
assertNotNull(parsed.getUnknownProperties());
assertEquals(1, parsed.getUnknownProperties().size());
assertEquals(new IntNode(3), parsed.getUnknownProperties().get("other_property"));
}

@Test
public void testSerializeJsonWithAnyGetter() throws JsonProcessingException {
JacksonAnyGetterType getterType = new JacksonAnyGetterType.Builder()
.setSimpleProperty("checkValue")
.putUnknownProperties("propertyOne", new TextNode("abc"))
.putUnknownProperties("propertyTwo", new IntNode(2)).build();

String json = objectMapper.writeValueAsString(getterType);
assertTrue("should contain simple_property",
json.contains("\"simple_property\":\"checkValue\""));
assertTrue("should contain propertyOne", json.contains("\"propertyOne\":\"abc\""));
assertTrue("should contain propertyTwo", json.contains("\"propertyTwo\":2"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,24 @@

class JacksonSupport {

private enum GenerateAnnotation {
DEFAULT,
JSON_ANY,
NONE
}

private static final String JSON_DESERIALIZE =
"com.fasterxml.jackson.databind.annotation.JsonDeserialize";
private static final QualifiedName JSON_PROPERTY =
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonProperty");
private static final String JACKSON_XML_ANNOTATION_PACKAGE =
"com.fasterxml.jackson.dataformat.xml.annotation";
private static final QualifiedName JSON_ANY_GETTER =
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonAnyGetter");
private static final QualifiedName JSON_ANY_SETTER =
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", "JsonAnyGetter"),
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonIgnore"),
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonUnwrapped"),
QualifiedName.of("com.fasterxml.jackson.annotation", "JsonValue"));
Expand All @@ -50,9 +59,20 @@ public void addJacksonAnnotations(
JSON_PROPERTY);
if (jsonPropertyAnnotation.isPresent()) {
resultBuilder.addAccessorAnnotations(Excerpts.add("%s%n", jsonPropertyAnnotation.get()));
} else if (generateDefaultAnnotations(getterMethod)) {
resultBuilder.addAccessorAnnotations(Excerpts.add(
"@%s(\"%s\")%n", JSON_PROPERTY, resultBuilder.getName()));
} else {
switch (generateDefaultAnnotations(getterMethod)) {
case DEFAULT:
resultBuilder.addAccessorAnnotations(Excerpts.add(
"@%s(\"%s\")%n", JSON_PROPERTY, resultBuilder.getName()));
break;
case JSON_ANY:
resultBuilder.addPutAnnotations(Excerpts.add("@%s%n", JSON_ANY_SETTER));
resultBuilder.addGetterAnnotations(Excerpts.add("@%s%n", JSON_ANY_GETTER));
break;
case NONE:
default:
break;
}
}

getterMethod
Expand All @@ -69,15 +89,18 @@ private boolean isXmlAnnotation(AnnotationMirror mirror) {
return pkg.contentEquals(JACKSON_XML_ANNOTATION_PACKAGE);
}

private static boolean generateDefaultAnnotations(ExecutableElement getterMethod) {
private static GenerateAnnotation generateDefaultAnnotations(ExecutableElement getterMethod) {
for (AnnotationMirror annotationMirror : getterMethod.getAnnotationMirrors()) {
TypeElement annotationTypeElement =
(TypeElement) (annotationMirror.getAnnotationType().asElement());
QualifiedName annotationType = QualifiedName.of(annotationTypeElement);
if (DISABLE_PROPERTY_ANNOTATIONS.contains(annotationType)) {
return false;
return GenerateAnnotation.NONE;
}
if (JSON_ANY_GETTER.equals(annotationType)) {
return GenerateAnnotation.JSON_ANY;
}
}
return true;
return GenerateAnnotation.DEFAULT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,9 @@ private void addPut(SourceBuilder code) {
}
code.add(" null\n");
}
code.addLine(" */")
.addLine("public %s %s(%s key, %s value) {",
code.addLine(" */");
addPutAnnotations(code);
code.addLine("public %s %s(%s key, %s value) {",
datatype.getBuilder(),
putMethod(property),
unboxedKeyType.orElse(keyType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ public Set<MergeAction> getMergeActions() {

@Override
public void addGetterAnnotations(SourceBuilder code) {
super.addGetterAnnotations(code);
for (TypeElement nullableAnnotation : nullables) {
code.add("@%s ", nullableAnnotation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ public FieldAccess getField() {
*/
public abstract ImmutableList<Excerpt> getAccessorAnnotations();

/**
* Returns a list of annotations that should be applied to the getter method of this
* property.
*/
public abstract ImmutableList<Excerpt> getGetterAnnotations();

/**
* Returns a list of annotations that should be applied to the put method of this
* property. This only applies to map properties.
* Annotation will be added to put(key, value) method.
*/
public abstract ImmutableList<Excerpt> getPutAnnotations();

public Property.Builder toBuilder() {
return new Builder().mergeFrom(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,11 @@ public void addPartialFieldAssignment(
public abstract Set<MergeAction> getMergeActions();

/** Adds method annotations for the value type getter method. */
public void addGetterAnnotations(@SuppressWarnings("unused") SourceBuilder code) {}
public void addGetterAnnotations(SourceBuilder code) {
for (Excerpt annotation : property.getGetterAnnotations()) {
code.add(annotation);
}
}

/** Adds a fragment converting the value object's field to the property's type. */
public void addReadValueFragment(SourceBuilder code, Excerpt finalField) {
Expand Down Expand Up @@ -203,6 +207,12 @@ public void addAccessorAnnotations(SourceBuilder code) {
}
}

public void addPutAnnotations(SourceBuilder code) {
for (Excerpt annotation : property.getPutAnnotations()) {
code.add(annotation);
}
}

@Override
public boolean equals(Object obj) {
if (obj == null || !getClass().isInstance(obj)) {
Expand Down

0 comments on commit 197d43f

Please sign in to comment.