From f23faa26f7ac7926a18bbc1eaf9af5b5052e8f8a Mon Sep 17 00:00:00 2001
From: mariofusco <mario.fusco@gmail.com>
Date: Mon, 11 Nov 2024 17:47:29 +0100
Subject: [PATCH] Fix deserialization of null maps in reflection-free Jackson
 deserializers

---
 .../processor/JacksonDeserializerFactory.java | 16 +++++-----
 .../jackson/deployment/test/MapWrapper.java   | 32 +++++++++++++++++++
 .../deployment/test/SimpleJsonResource.java   |  7 ++++
 .../deployment/test/SimpleJsonTest.java       | 16 +++++++++-
 ...JsonWithReflectionFreeSerializersTest.java |  2 +-
 5 files changed, 63 insertions(+), 10 deletions(-)
 create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/MapWrapper.java

diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java
index fa1fdb002bf0a..ebc47ab056de6 100644
--- a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java
+++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java
@@ -239,18 +239,18 @@ private boolean deserializeObject(ClassInfo classInfo, ResultHandle objHandle, C
         ResultHandle nextField = loopCreator
                 .invokeInterfaceMethod(ofMethod(Iterator.class, "next", Object.class), fieldsIterator);
         ResultHandle mapEntry = loopCreator.checkCast(nextField, Map.Entry.class);
-        ResultHandle fieldName = loopCreator
-                .invokeInterfaceMethod(ofMethod(Map.Entry.class, "getKey", Object.class), mapEntry);
         ResultHandle fieldValue = loopCreator.checkCast(loopCreator
                 .invokeInterfaceMethod(ofMethod(Map.Entry.class, "getValue", Object.class), mapEntry), JsonNode.class);
 
-        loopCreator.ifTrue(loopCreator.invokeVirtualMethod(ofMethod(JsonNode.class, "isNull", boolean.class), fieldValue))
-                .trueBranch().continueScope(loopCreator);
+        BytecodeCreator fieldReader = loopCreator
+                .ifTrue(loopCreator.invokeVirtualMethod(ofMethod(JsonNode.class, "isNull", boolean.class), fieldValue))
+                .falseBranch();
+
+        ResultHandle fieldName = fieldReader
+                .invokeInterfaceMethod(ofMethod(Map.Entry.class, "getKey", Object.class), mapEntry);
+        Switch.StringSwitch strSwitch = fieldReader.stringSwitch(fieldName);
 
-        Set<String> deserializedFields = new HashSet<>();
-        ResultHandle deserializationContext = deserialize.getMethodParam(1);
-        Switch.StringSwitch strSwitch = loopCreator.stringSwitch(fieldName);
-        return deserializeFields(classCreator, classInfo, deserializationContext, objHandle, fieldValue, deserializedFields,
+        return deserializeFields(classCreator, classInfo, deserialize.getMethodParam(1), objHandle, fieldValue, new HashSet<>(),
                 strSwitch, parseTypeParameters(classInfo, classCreator));
     }
 
diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/MapWrapper.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/MapWrapper.java
new file mode 100644
index 0000000000000..6bc5bda55d642
--- /dev/null
+++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/MapWrapper.java
@@ -0,0 +1,32 @@
+package io.quarkus.resteasy.reactive.jackson.deployment.test;
+
+import java.util.Map;
+
+public class MapWrapper {
+
+    private String name;
+    private Map<String, String> properties;
+
+    public MapWrapper() {
+    }
+
+    public MapWrapper(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(Map<String, String> properties) {
+        this.properties = properties;
+    }
+}
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 855c43625c09e..861f01ce08a96 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
@@ -123,6 +123,13 @@ public StateRecord echoDog(StateRecord stateRecord) {
         return stateRecord;
     }
 
+    @POST
+    @Path("/null-map-echo")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public MapWrapper echoNullMap(MapWrapper mapWrapper) {
+        return mapWrapper;
+    }
+
     @EnableSecureSerialization
     @GET
     @Path("/abstract-cat")
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 d2f22569f9a7a..a5fa4d498c923 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
@@ -36,7 +36,7 @@ public JavaArchive get() {
                                     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, ContainerDTO.class,
-                                    NestedInterface.class, StateRecord.class)
+                                    NestedInterface.class, StateRecord.class, MapWrapper.class)
                             .addAsResource(new StringAsset("admin-expression=admin\n" +
                                     "user-expression=user\n" +
                                     "birth-date-roles=alice,bob\n"), "application.properties");
@@ -733,4 +733,18 @@ public void testRecordEcho() {
         assertTrue(first >= 0);
         assertEquals(first, last);
     }
+
+    @Test
+    public void testNullMapEcho() {
+        RestAssured
+                .with()
+                .body(new MapWrapper("test"))
+                .contentType("application/json; charset=utf-8")
+                .post("/simple/null-map-echo")
+                .then()
+                .statusCode(200)
+                .contentType("application/json")
+                .body("name", Matchers.is("test"))
+                .body("properties", Matchers.nullValue());
+    }
 }
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 65dec05aa59a4..10ea3d373ce91 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
@@ -25,7 +25,7 @@ public JavaArchive get() {
                                     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, ContainerDTO.class,
-                                    NestedInterface.class, StateRecord.class)
+                                    NestedInterface.class, StateRecord.class, MapWrapper.class)
                             .addAsResource(new StringAsset("admin-expression=admin\n" +
                                     "user-expression=user\n" +
                                     "birth-date-roles=alice,bob\n" +