From 7c81d83b288c82c71905928c98ec73f1246e2b0f Mon Sep 17 00:00:00 2001 From: mariofusco Date: Fri, 30 Aug 2024 18:10:53 +0200 Subject: [PATCH] Fix Jackson serializers generation for interfaces and boxed primitive types --- .../processor/JacksonSerializerFactory.java | 37 +++++++++++++++++-- .../reactive/jackson/deployment/test/Cat.java | 4 ++ .../jackson/deployment/test/ContainerDTO.java | 4 ++ .../deployment/test/NestedInterface.java | 28 ++++++++++++++ .../deployment/test/SimpleJsonResource.java | 6 +++ .../deployment/test/SimpleJsonTest.java | 17 ++++++++- ...JsonWithReflectionFreeSerializersTest.java | 3 +- 7 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ContainerDTO.java create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/NestedInterface.java diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonSerializerFactory.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonSerializerFactory.java index 7ebd7ae9b40a4..691d102c35ddf 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonSerializerFactory.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonSerializerFactory.java @@ -318,7 +318,7 @@ private void writeField(ClassInfo classInfo, FieldSpecs fieldSpecs, BytecodeCrea if (primitiveMethodName != null) { MethodDescriptor primitiveWriter = MethodDescriptor.ofMethod(JSON_GEN_CLASS_NAME, primitiveMethodName, "void", - typeName); + fieldSpecs.writtenType()); bytecode.invokeVirtualMethod(primitiveWriter, jsonGenerator, arg); return; } @@ -372,7 +372,7 @@ private void registerTypeToBeGenerated(String typeName) { private String writeMethodForPrimitiveFields(String typeName) { return switch (typeName) { - case "java.lang.String" -> "writeString"; + case "java.lang.String", "char", "java.lang.Character" -> "writeString"; case "short", "java.lang.Short", "int", "java.lang.Integer", "long", "java.lang.Long", "float", "java.lang.Float", "double", "java.lang.Double" -> "writeNumber"; @@ -590,8 +590,37 @@ boolean hasUnknownAnnotation() { } ResultHandle toValueReaderHandle(BytecodeCreator bytecode, ResultHandle valueHandle) { - return methodInfo != null ? bytecode.invokeVirtualMethod(MethodDescriptor.of(methodInfo), valueHandle) - : bytecode.readInstanceField(FieldDescriptor.of(fieldInfo), valueHandle); + ResultHandle handle = accessorHandle(bytecode, valueHandle); + + handle = switch (fieldType.name().toString()) { + case "char", "java.lang.Character" -> bytecode.invokeStaticMethod( + MethodDescriptor.ofMethod(Character.class, "toString", String.class, char.class), handle); + default -> handle; + }; + + return handle; + } + + private ResultHandle accessorHandle(BytecodeCreator bytecode, ResultHandle valueHandle) { + if (methodInfo != null) { + if (methodInfo.declaringClass().isInterface()) { + return bytecode.invokeInterfaceMethod(MethodDescriptor.of(methodInfo), valueHandle); + } + return bytecode.invokeVirtualMethod(MethodDescriptor.of(methodInfo), valueHandle); + } + return bytecode.readInstanceField(FieldDescriptor.of(fieldInfo), valueHandle); + } + + String writtenType() { + return switch (fieldType.name().toString()) { + case "char", "java.lang.Character" -> "java.lang.String"; + case "java.lang.Integer" -> "int"; + case "java.lang.Short" -> "short"; + case "java.lang.Long" -> "long"; + case "java.lang.Double" -> "double"; + case "java.lang.Float" -> "float"; + default -> fieldType.name().toString(); + }; } private String[] rolesAllowed() { diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cat.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cat.java index e2aa7a01955cc..ab57f3ea4d3c4 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cat.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/Cat.java @@ -14,4 +14,8 @@ public int getPrivateAge() { public void setPrivateAge(int privateAge) { this.privateAge = privateAge; } + + public char getInitial() { + return getPublicName().charAt(0); + } } diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ContainerDTO.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ContainerDTO.java new file mode 100644 index 0000000000000..7638012b16f01 --- /dev/null +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/ContainerDTO.java @@ -0,0 +1,4 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +public record ContainerDTO(NestedInterface nestedInterface) { +} diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/NestedInterface.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/NestedInterface.java new file mode 100644 index 0000000000000..aae283c86bafe --- /dev/null +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/NestedInterface.java @@ -0,0 +1,28 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +public interface NestedInterface { + + NestedInterface INSTANCE = new NestedInterface() { + @Override + public String getString() { + return "response"; + } + + @Override + public Integer getInt() { + return 42; + } + + @Override + public char getCharacter() { + return 'a'; + } + }; + + String getString(); + + Integer getInt(); + + char getCharacter(); + +} diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java index a7da6fb828626..6a48584164760 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java @@ -442,6 +442,12 @@ public String genericInputTest(DataItem item) { return item.getContent().getName(); } + @GET + @Path("/interface") + public ContainerDTO interfaceTest() { + return new ContainerDTO(NestedInterface.INSTANCE); + } + public static class UnquotedFieldsPersonSerialization implements BiFunction { public static final AtomicInteger count = new AtomicInteger(); diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java index 0c9cdff2a766d..3db48396587fa 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java @@ -34,7 +34,8 @@ public JavaArchive get() { NoopReaderInterceptor.class, TestIdentityProvider.class, TestIdentityController.class, AbstractPet.class, Dog.class, Cat.class, Veterinarian.class, AbstractNamedPet.class, AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class, - Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class) + Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class, + NestedInterface.class) .addAsResource(new StringAsset("admin-expression=admin\n" + "user-expression=user\n" + "birth-date-roles=alice,bob\n"), "application.properties"); @@ -506,6 +507,18 @@ public void testGenericInput() { .body(is("foo")); } + @Test + public void testInterface() { + RestAssured + .with() + .get("/simple/interface") + .then() + .statusCode(200) + .body("nestedInterface.int", Matchers.is(42)) + .body("nestedInterface.character", Matchers.is("a")) + .body("nestedInterface.string", Matchers.is("response")); + } + @Test public void testSecureFieldOnAbstractClass() { // implementor with / without @SecureField returned @@ -602,6 +615,7 @@ private static void testSecuredFieldOnAbstractClass(String catPath, String dogPa .then() .statusCode(200) .body("publicName", Matchers.is("Garfield")) + .body("initial", Matchers.is("G")) .body("privateName", Matchers.nullValue()) .body("veterinarian.name", Matchers.is("Dolittle")) .body("veterinarian.title", Matchers.nullValue()) @@ -625,6 +639,7 @@ private static void testSecuredFieldOnAbstractClass(String catPath, String dogPa .then() .statusCode(200) .body("publicName", Matchers.is("Garfield")) + .body("initial", Matchers.is("G")) .body("privateName", Matchers.is("Monday")) .body("privateAge", Matchers.is(4)) .body("veterinarian.name", Matchers.is("Dolittle")) diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java index 24fa1af4fb106..0254e48598fe8 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java @@ -24,7 +24,8 @@ public JavaArchive get() { NoopReaderInterceptor.class, TestIdentityProvider.class, TestIdentityController.class, AbstractPet.class, Dog.class, Cat.class, Veterinarian.class, AbstractNamedPet.class, AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class, - Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class) + Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class, + NestedInterface.class) .addAsResource(new StringAsset("admin-expression=admin\n" + "user-expression=user\n" + "birth-date-roles=alice,bob\n" +