diff --git a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/implementation/ObjectMapperShim.java b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/implementation/ObjectMapperShim.java index 0ba4f95ca0a7b..7b772355ec1cd 100644 --- a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/implementation/ObjectMapperShim.java +++ b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/implementation/ObjectMapperShim.java @@ -6,10 +6,12 @@ import com.azure.core.annotation.HeaderCollection; import com.azure.core.http.HttpHeader; import com.azure.core.http.HttpHeaders; -import com.azure.core.implementation.ReflectiveInvoker; +import com.azure.core.implementation.ReflectionSerializable; import com.azure.core.implementation.ReflectionUtils; +import com.azure.core.implementation.ReflectiveInvoker; import com.azure.core.implementation.TypeUtil; import com.azure.core.util.logging.ClientLogger; +import com.azure.json.JsonSerializable; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -264,6 +266,7 @@ public JsonNode readTree(byte[] content) throws IOException { } } + @SuppressWarnings("unchecked") private JavaType createJavaType(Type type) { if (type == null) { return null; @@ -280,7 +283,28 @@ private JavaType createJavaType(Type type) { return getFromTypeCache(type, t -> mapper.getTypeFactory() .constructParametricType((Class) parameterizedType.getRawType(), javaTypeArguments)); } else { - return getFromTypeCache(type, t -> mapper.getTypeFactory().constructType(t)); + return getFromTypeCache(type, t -> { + JavaType javaType = mapper.constructType(t); + + // Need additional handling here so that the JavaType returned has the correct value handler for + // JsonSerializable types. + // While JsonSerializableDeserializer is registered with the ObjectMapper, and it mutates the + // JsonSerializer used by Jackson to handle as a JsonSerializable type, there have been cases where + // collection types (List, Map, etc) have not been handled correctly. So, additional handling is done + // here to ensure that the JavaType returned has the correct value handler. + + if (!(t instanceof Class)) { + // Not a Class, so can't be a JsonSerializable type. + return javaType; + } + + if (ReflectionSerializable.supportsJsonSerializable((Class) t)) { + // JsonSerializable type, so add the JsonSerializableDeserializer as the value handler. + return javaType.withValueHandler(new JsonSerializableDeserializer((Class>) t)); + } + + return javaType; + }); } } diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperShim.java b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperShim.java index b6a7af7a63724..4a2c2ac214cb1 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperShim.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/implementation/jackson/ObjectMapperShim.java @@ -6,12 +6,14 @@ import com.azure.core.annotation.HeaderCollection; import com.azure.core.http.HttpHeader; import com.azure.core.http.HttpHeaders; +import com.azure.core.implementation.ReflectionSerializable; import com.azure.core.implementation.ReflectionUtils; import com.azure.core.implementation.ReflectiveInvoker; import com.azure.core.implementation.TypeUtil; import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LogLevel; import com.azure.core.util.serializer.MemberNameConverter; +import com.azure.json.JsonSerializable; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -294,6 +296,7 @@ public JsonNode readTree(byte[] content) throws IOException { } } + @SuppressWarnings("unchecked") private JavaType createJavaType(Type type) { if (type == null) { return null; @@ -310,7 +313,28 @@ private JavaType createJavaType(Type type) { return getFromTypeCache(type, t -> mapper.getTypeFactory() .constructParametricType((Class) parameterizedType.getRawType(), javaTypeArguments)); } else { - return getFromTypeCache(type, t -> mapper.getTypeFactory().constructType(t)); + return getFromTypeCache(type, t -> { + JavaType javaType = mapper.constructType(t); + + // Need additional handling here so that the JavaType returned has the correct value handler for + // JsonSerializable types. + // While JsonSerializableDeserializer is registered with the ObjectMapper, and it mutates the + // JsonSerializer used by Jackson to handle as a JsonSerializable type, there have been cases where + // collection types (List, Map, etc) have not been handled correctly. So, additional handling is done + // here to ensure that the JavaType returned has the correct value handler. + + if (!(t instanceof Class)) { + // Not a Class, so can't be a JsonSerializable type. + return javaType; + } + + if (ReflectionSerializable.supportsJsonSerializable((Class) t)) { + // JsonSerializable type, so add the JsonSerializableDeserializer as the value handler. + return javaType.withValueHandler(new JsonSerializableDeserializer((Class>) t)); + } + + return javaType; + }); } }