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

Fix #104: Support @JsonAnyGetter #417

Merged
merged 1 commit into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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