From 36a2d73fd201eff3dbfa4dc2aeffe39d917a6eb2 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 26 Aug 2020 10:42:15 +0200 Subject: [PATCH 1/2] Register Jackson @JsonNaming strategies for reflection Fix #11397 --- .../jackson/deployment/JacksonProcessor.java | 9 +++++ .../ModelWithJsonNamingStrategyResource.java | 28 +++++++++++++++ .../it/jackson/model/SampleResponse.java | 35 +++++++++++++++++++ ...ModelWithJsonNamingStrategyResourceIT.java | 9 +++++ ...delWithJsonNamingStrategyResourceTest.java | 24 +++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 integration-tests/jackson/src/main/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResource.java create mode 100644 integration-tests/jackson/src/main/java/io/quarkus/it/jackson/model/SampleResponse.java create mode 100644 integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceIT.java create mode 100644 integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceTest.java diff --git a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java index d41d1406b765f..50568388b0f8f 100755 --- a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java +++ b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java @@ -53,6 +53,7 @@ public class JacksonProcessor { private static final DotName JSON_DESERIALIZE = DotName.createSimple(JsonDeserialize.class.getName()); private static final DotName JSON_SERIALIZE = DotName.createSimple(JsonSerialize.class.getName()); private static final DotName JSON_CREATOR = DotName.createSimple("com.fasterxml.jackson.annotation.JsonCreator"); + private static final DotName JSON_NAMING = DotName.createSimple("com.fasterxml.jackson.databind.annotation.JsonNaming"); private static final DotName BUILDER_VOID = DotName.createSimple(Void.class.getName()); private static final String TIME_MODULE = "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"; @@ -140,6 +141,14 @@ CapabilityBuildItem register() { } } + // register @JsonNaming strategy implementations for reflection + for (AnnotationInstance jsonNamingInstance : index.getAnnotations(JSON_NAMING)) { + AnnotationValue strategyValue = jsonNamingInstance.value("value"); + if (strategyValue != null) { + reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, strategyValue.asClass().name().toString())); + } + } + // this needs to be registered manually since the runtime module is not indexed by Jandex additionalBeans.produce(new AdditionalBeanBuildItem(ObjectMapperProducer.class)); diff --git a/integration-tests/jackson/src/main/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResource.java b/integration-tests/jackson/src/main/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResource.java new file mode 100644 index 0000000000000..ef3692133a658 --- /dev/null +++ b/integration-tests/jackson/src/main/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResource.java @@ -0,0 +1,28 @@ +package io.quarkus.it.jackson; + +import java.io.IOException; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.quarkus.it.jackson.model.SampleResponse; + +@Path("/json-naming/") +public class ModelWithJsonNamingStrategyResource { + + private final ObjectMapper objectMapper; + + public ModelWithJsonNamingStrategyResource(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public String get() throws IOException { + return objectMapper.writeValueAsString(new SampleResponse("My blog post")); + } +} diff --git a/integration-tests/jackson/src/main/java/io/quarkus/it/jackson/model/SampleResponse.java b/integration-tests/jackson/src/main/java/io/quarkus/it/jackson/model/SampleResponse.java new file mode 100644 index 0000000000000..6badef9590475 --- /dev/null +++ b/integration-tests/jackson/src/main/java/io/quarkus/it/jackson/model/SampleResponse.java @@ -0,0 +1,35 @@ +package io.quarkus.it.jackson.model; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +@RegisterForReflection +public class SampleResponse { + + private String blogTitle; + + public SampleResponse() { + } + + public SampleResponse(String blogTitle) { + this.blogTitle = blogTitle; + } + + public String getBlogTitle() { + return blogTitle; + } + + public void setBlogTitle(String blogTitle) { + this.blogTitle = blogTitle; + } + + @Override + public String toString() { + return "SampleResponse{" + + "blogTitle='" + blogTitle + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceIT.java b/integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceIT.java new file mode 100644 index 0000000000000..9411062a9e385 --- /dev/null +++ b/integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceIT.java @@ -0,0 +1,9 @@ +package io.quarkus.it.jackson; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class ModelWithJsonNamingStrategyResourceIT extends ModelWithJsonNamingStrategyResourceTest { + + // Execute the same tests but in native mode. +} diff --git a/integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceTest.java b/integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceTest.java new file mode 100644 index 0000000000000..0eec2037f7cb1 --- /dev/null +++ b/integration-tests/jackson/src/test/java/io/quarkus/it/jackson/ModelWithJsonNamingStrategyResourceTest.java @@ -0,0 +1,24 @@ +package io.quarkus.it.jackson; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class ModelWithJsonNamingStrategyResourceTest { + + @Test + public void testStrategy() throws IOException { + given() + .when().get("/json-naming/") + .then() + .statusCode(200) + .body(containsString("blog_title")); + } + +} From 7800adb84d74aa0ed0ac2230a1f3d26f564a169a Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 26 Aug 2020 10:55:51 +0200 Subject: [PATCH 2/2] Don't use .toString() directly on Type, use .name().toString() --- .../java/io/quarkus/jackson/deployment/JacksonProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java index 50568388b0f8f..3908aa8582dfb 100755 --- a/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java +++ b/extensions/jackson/deployment/src/main/java/io/quarkus/jackson/deployment/JacksonProcessor.java @@ -118,7 +118,7 @@ CapabilityBuildItem register() { AnnotationValue usingValue = deserializeInstance.value("using"); if (usingValue != null) { // the Deserializers are constructed internally by Jackson using a no-args constructor - reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, usingValue.asClass().toString())); + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, usingValue.asClass().name().toString())); } } @@ -129,7 +129,7 @@ CapabilityBuildItem register() { AnnotationValue usingValue = serializeInstance.value("using"); if (usingValue != null) { // the Deserializers are constructed internally by Jackson using a no-args constructor - reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, usingValue.asClass().toString())); + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, usingValue.asClass().name().toString())); } } }