diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 715dd5fbd1b3..a6aa977c92d7 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -32,6 +32,7 @@ com.azure:azure-messaging-eventhubs;5.1.2;5.2.0-beta.1 com.azure:azure-messaging-eventhubs-checkpointstore-blob;1.1.2;1.2.0-beta.1 com.azure:azure-messaging-servicebus;7.0.0-beta.3;7.0.0-beta.4 com.azure:azure-search-documents;11.0.0;11.1.0-beta.1 +com.azure:azure-search-documents-serializer-json-jackson;1.0.0;1.0.0-beta.1 com.azure:azure-security-keyvault-certificates;4.0.4;4.1.0-beta.5 com.azure:azure-security-keyvault-keys;4.1.4;4.2.0-beta.6 com.azure:azure-security-keyvault-secrets;4.1.4;4.2.0-beta.5 @@ -72,6 +73,11 @@ com.microsoft.azure:spring-data-cosmosdb;3.0.0-beta.1;3.0.0-beta.1 unreleased_com.azure:azure-messaging-servicebus;7.0.0-beta.4 unreleased_com.azure:azure-security-keyvault-keys;4.2.0-beta.6 +unreleased_com.azure:azure-core-experimental;1.0.0-beta.2 +unreleased_com.azure:azure-core-serializer-json-jackson;1.0.0-beta.3 +unreleased_com.azure:azure-core-serializer-json-gson;1.0.0-beta.3 +unreleased_com.azure-search-documents-serializer-json-jackson;1.0.0-beta.1 +unreleased_com.azure-search-documents-serializer-json-gson;1.0.0-beta.1 # Released Beta dependencies: Copy the entry from above, prepend "beta_", remove the current # version and set the version to the released beta. Released beta dependencies are only valid diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonInclusion.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonInclusion.java new file mode 100644 index 000000000000..4e3ee6e8945b --- /dev/null +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonInclusion.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.experimental.serializer; + +/** + * The enum to indicate how to include + */ +public enum JsonInclusion { + ALWAYS, + DEFAULT +} diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonOptions.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonOptions.java new file mode 100644 index 000000000000..bb92fd66c61a --- /dev/null +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonOptions.java @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.experimental.serializer; + +/** + * The json serializer options. + */ +public class JsonOptions { + private JsonInclusion jsonInclusion; + + /** + * Gets the json inclusion value. + * @return The enum field json inclusion. + */ + public JsonInclusion getJsonInclusion() { + return jsonInclusion; + } + + /** + * Sets the json inclusion value. + * @param jsonInclusion The enum field json inclusion. + * @return The {@link JsonOptions} object itself. + */ + public JsonOptions setJsonInclusion(JsonInclusion jsonInclusion) { + this.jsonInclusion = jsonInclusion; + return this; + } +} diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializer.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializer.java index e31fc552ae76..db214ae83fe6 100644 --- a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializer.java +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializer.java @@ -23,6 +23,17 @@ public interface JsonSerializer extends ObjectSerializer { @Override Mono deserialize(InputStream stream, Class clazz); + /** + * Reads a JSON stream into its object representation. + * + * @param stream JSON stream. + * @param toType {@link Type} The type Json stream converts to. + * @param Type of the object. + * @return The object represented by the deserialized JSON stream. + */ + @Override + Mono deserialize(InputStream stream, Type toType); + /** * Reads a JSON tree into its object representation. * @@ -68,4 +79,25 @@ public interface JsonSerializer extends ObjectSerializer { * @return The JSON tree representing the object. */ Mono toTree(Object value); + + + /** + * Converts a Java object into a target {@link Type}. + * + * @param fromValue The value which converts from + * @param toType The type which converts to + * @param The generic type to covert to. + * @return The JSON tree representing the deserialized JSON byte array. + */ + Mono convertValue(Object fromValue, Type toType); + + /** + * Converts a Java object into a target model class. + * + * @param fromValue The value which converts from + * @param clazz Representing the class. + * @param The generic type to covert to. + * @return The JSON tree representing the deserialized JSON byte array. + */ + Mono convertValue(Object fromValue, Class clazz); } diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProvider.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProvider.java index d340c07a91bb..55e8413e71b1 100644 --- a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProvider.java +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProvider.java @@ -14,4 +14,13 @@ public interface JsonSerializerProvider { * @return A new {@link JsonSerializer} instance. */ JsonSerializer createInstance(); + + + /** + * Creates a new instance of the {@link JsonSerializer} that this JsonSerializerProvider is configured to create. + * + * @param jsonOptions The json options for the serializer. + * @return A new {@link JsonSerializer} instance. + */ + JsonSerializer createInstance(JsonOptions jsonOptions); } diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProviders.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProviders.java index 43eccdd1cfd7..6590c72ddf2c 100644 --- a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProviders.java +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/JsonSerializerProviders.java @@ -30,6 +30,22 @@ public static JsonSerializer createInstance() { return defaultProvider.createInstance(); } + + /** + * Creates an instance of {@link JsonSerializer} using the first {@link JsonSerializerProvider} found in the + * classpath. + * + * @param jsonOptions The json options for the serializer. + * @return A new instance of {@link JsonSerializer}. + */ + public static JsonSerializer createInstance(JsonOptions jsonOptions) { + if (defaultProvider == null) { + loadFromClasspath(); + } + + return defaultProvider.createInstance(jsonOptions); + } + private static synchronized void loadFromClasspath() { if (attemptedLoad && defaultProvider != null) { return; diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/ObjectSerializer.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/ObjectSerializer.java index 10d8503612ea..3e3771276297 100644 --- a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/ObjectSerializer.java +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/ObjectSerializer.java @@ -22,6 +22,16 @@ public interface ObjectSerializer { */ Mono deserialize(InputStream stream, Class clazz); + /** + * Reads a stream into its object representation. + * + * @param stream {@link InputStream} of data. + * @param type {@link Type} The type Json stream converts to. + * @param Type of the object. + * @return The object represented by the deserialized stream. + */ + Mono deserialize(InputStream stream, Type type); + /** * Writes the object into a stream. * diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/Type.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/Type.java new file mode 100644 index 000000000000..e540465a1a26 --- /dev/null +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/serializer/Type.java @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.core.experimental.serializer; + +import java.lang.reflect.ParameterizedType; + +public abstract class Type { + + private final java.lang.reflect.Type javaType; + + /** + * Constructor + * @throws IllegalArgumentException if the reference is constructed without actual type information + */ + public Type() { + java.lang.reflect.Type superClass = this.getClass().getGenericSuperclass(); + if (superClass instanceof Class) { + throw new IllegalArgumentException( + "Internal error: TypeReference constructed without actual type information"); + } else { + this.javaType = ((ParameterizedType) superClass).getActualTypeArguments()[0]; + } + } + + /** + * Return class T type + * @return type + */ + public java.lang.reflect.Type getJavaType() { + return javaType; + } + + /** + * Returns is the type a ParameterizedType (collection, string etc.) + * @return boolean value + */ + public boolean isParameterizedType() { + if (!(javaType instanceof ParameterizedType)) { + return false; + } + + return true; + } + + /** + * Get the type of the actual type of the Type object + * @return ava.lang.reflect.Type + */ + public java.lang.reflect.Type getListType() { + assert isParameterizedType(); + ParameterizedType parameterizedType = (ParameterizedType) javaType; + return parameterizedType.getActualTypeArguments()[0]; + } +} diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometryDeserializer.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometryDeserializer.java index 72fd9b228d25..c16b34a90b6c 100644 --- a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometryDeserializer.java +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometryDeserializer.java @@ -20,7 +20,7 @@ /** * Deserializes a JSON object into a {@link Geometry}. */ -final class GeometryDeserializer extends JsonDeserializer { +public final class GeometryDeserializer extends JsonDeserializer { private static final ClientLogger LOGGER = new ClientLogger(GeometryDeserializer.class); /* @@ -60,6 +60,10 @@ final class GeometryDeserializer extends JsonDeserializer { .addDeserializer(CollectionGeometry.class, geometrySubclassDeserializer(CollectionGeometry.class)); } + public static SimpleModule getModule() { + return MODULE; + } + @Override public Geometry deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return read(ctxt.readTree(p)); diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometrySerializer.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometrySerializer.java index 1bd73a93aad8..c634d191a85f 100644 --- a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometrySerializer.java +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/spatial/GeometrySerializer.java @@ -17,14 +17,19 @@ /** * Serializes a {@link Geometry} into JSON. */ -final class GeometrySerializer extends JsonSerializer { +public final class GeometrySerializer extends JsonSerializer { private static final ClientLogger LOGGER = new ClientLogger(GeometrySerializer.class); - static final SimpleModule MODULE; - - static { - MODULE = new SimpleModule(); - MODULE.addSerializer(Geometry.class, new GeometrySerializer()); + /** + * Gets a module wrapping this serializer as an adapter for the Jackson + * ObjectMapper. + * + * @return a simple module to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + SimpleModule module = new SimpleModule(); + module.addSerializer(Geometry.class, new GeometrySerializer()); + return module; } @Override diff --git a/sdk/core/azure-core-experimental/src/main/java/module-info.java b/sdk/core/azure-core-experimental/src/main/java/module-info.java index 36066c5c3e38..1b3efab2b57a 100644 --- a/sdk/core/azure-core-experimental/src/main/java/module-info.java +++ b/sdk/core/azure-core-experimental/src/main/java/module-info.java @@ -7,6 +7,7 @@ exports com.azure.core.experimental.serializer; exports com.azure.core.experimental.spatial; + opens com.azure.core.experimental.spatial to com.fasterxml.jackson.databind; uses com.azure.core.experimental.serializer.AvroSerializerProvider; uses com.azure.core.experimental.serializer.JsonSerializerProvider; } diff --git a/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/spatial/GeometrySerializerTests.java b/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/spatial/GeometrySerializerTests.java index ab1b9207281f..177ddd9e97de 100644 --- a/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/spatial/GeometrySerializerTests.java +++ b/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/spatial/GeometrySerializerTests.java @@ -28,7 +28,7 @@ public class GeometrySerializerTests { private static final ObjectMapper MAPPER; static { - MAPPER = new ObjectMapper().registerModule(GeometrySerializer.MODULE); + MAPPER = new ObjectMapper().registerModule(GeometrySerializer.getModule()); } @Test diff --git a/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializer.java b/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializer.java index ca31f9c01921..5c508f5e454a 100644 --- a/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializer.java +++ b/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializer.java @@ -5,6 +5,7 @@ import com.azure.core.experimental.serializer.JsonNode; import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.Type; import com.google.gson.Gson; import com.google.gson.JsonParser; import reactor.core.publisher.Mono; @@ -36,6 +37,12 @@ public Mono deserialize(InputStream stream, Class clazz) { return Mono.fromCallable(() -> gson.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), clazz)); } + @Override + public Mono deserialize(InputStream stream, Type toType) { + return Mono.fromCallable(() -> gson.fromJson(new InputStreamReader(stream, StandardCharsets.UTF_8), + toType.getJavaType())); + } + @Override public Mono deserializeTree(JsonNode jsonNode, Class clazz) { return Mono.fromCallable(() -> gson.fromJson(JsonNodeUtils.toGsonElement(jsonNode), clazz)); @@ -68,5 +75,14 @@ public Mono toTree(Object value) { return Mono.fromCallable(() -> JsonNodeUtils.fromGsonElement(gson.toJsonTree(value))); } + @Override + public Mono convertValue(Object fromValue, Type toType) { + return Mono.fromCallable(() -> gson.fromJson(gson.toJson(fromValue), toType.getJavaType())); + } + + @Override + public Mono convertValue(Object fromValue, Class clazz) { + return Mono.fromCallable(() -> gson.fromJson(gson.toJson(fromValue), clazz)); + } } diff --git a/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerBuilder.java b/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerBuilder.java index aac9c37447c4..61eb4309bcdd 100644 --- a/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerBuilder.java +++ b/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerBuilder.java @@ -3,6 +3,8 @@ package com.azure.core.serializer.json.gson; +import com.azure.core.experimental.serializer.JsonInclusion; +import com.azure.core.experimental.serializer.JsonOptions; import com.google.gson.Gson; /** @@ -10,6 +12,7 @@ */ public final class GsonJsonSerializerBuilder { private Gson gson; + private JsonOptions options; /** * Constructs a new instance of {@link GsonJsonSerializer} with the configurations set in this builder. @@ -17,9 +20,12 @@ public final class GsonJsonSerializerBuilder { * @return A new instance of {@link GsonJsonSerializer}. */ public GsonJsonSerializer build() { - return (gson == null) - ? new GsonJsonSerializer(new Gson()) - : new GsonJsonSerializer(gson); + Gson localGson = (gson == null) ? new Gson() : gson; + + if (options != null && options.getJsonInclusion() == JsonInclusion.ALWAYS) { + localGson.serializeNulls(); + } + return new GsonJsonSerializer(localGson); } /** @@ -34,4 +40,18 @@ public GsonJsonSerializerBuilder serializer(Gson gson) { this.gson = gson; return this; } + + + /** + * Sets the {@link Gson} that will be used during serialization. + *

+ * If this is set to {@code null} the default {@link Gson} will be used. + * + * @param options {@link Gson} that will be used during serialization. + * @return The updated GsonJsonSerializerBuilder class. + */ + public GsonJsonSerializerBuilder options(JsonOptions options) { + this.options = options; + return this; + } } diff --git a/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerProvider.java b/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerProvider.java index 7224b78c9b83..7026f613bb59 100644 --- a/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerProvider.java +++ b/sdk/core/azure-core-serializer-json-gson/src/main/java/com/azure/core/serializer/json/gson/GsonJsonSerializerProvider.java @@ -3,6 +3,7 @@ package com.azure.core.serializer.json.gson; +import com.azure.core.experimental.serializer.JsonOptions; import com.azure.core.experimental.serializer.JsonSerializer; import com.azure.core.experimental.serializer.JsonSerializerProvider; @@ -14,4 +15,9 @@ public class GsonJsonSerializerProvider implements JsonSerializerProvider { public JsonSerializer createInstance() { return new GsonJsonSerializerBuilder().build(); } + + @Override + public JsonSerializer createInstance(JsonOptions jsonOptions) { + return new GsonJsonSerializerBuilder().options(jsonOptions).build(); + } } diff --git a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializer.java b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializer.java index 42a298025ce1..c00200175eaf 100644 --- a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializer.java +++ b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializer.java @@ -5,7 +5,9 @@ import com.azure.core.experimental.serializer.JsonNode; import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.Type; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; import reactor.core.publisher.Mono; import java.io.InputStream; @@ -16,6 +18,7 @@ */ public final class JacksonJsonSerializer implements JsonSerializer { private final ObjectMapper mapper; + private final TypeFactory typeFactory; /** * Constructs a {@link JsonSerializer} using the passed Jackson serializer. @@ -24,6 +27,7 @@ public final class JacksonJsonSerializer implements JsonSerializer { */ JacksonJsonSerializer(ObjectMapper mapper) { this.mapper = mapper; + typeFactory = mapper.getTypeFactory(); } @Override @@ -37,6 +41,17 @@ public Mono deserialize(InputStream stream, Class clazz) { }); } + @Override + public Mono deserialize(InputStream stream, Type toType) { + return Mono.fromCallable(() -> { + if (stream == null) { + return null; + } + + return mapper.readValue(stream, typeFactory.constructType(toType.getJavaType())); + }); + } + @Override public Mono deserializeTree(JsonNode jsonNode, Class clazz) { return Mono.fromCallable(() -> mapper.treeToValue(JsonNodeUtils.toJacksonNode(jsonNode), clazz)); @@ -65,4 +80,14 @@ public Mono toTree(InputStream stream) { public Mono toTree(Object value) { return Mono.fromCallable(() -> JsonNodeUtils.fromJacksonNode(mapper.valueToTree(value))); } + + @Override + public Mono convertValue(Object fromValue, Type toType) { + return Mono.fromCallable(() -> mapper.convertValue(fromValue, typeFactory.constructType(toType.getJavaType()))); + } + + @Override + public Mono convertValue(Object fromValue, Class clazz) { + return Mono.fromCallable(() -> mapper.convertValue(fromValue, clazz)); + } } diff --git a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerBuilder.java b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerBuilder.java index d752ccb66004..8818bd8972f0 100644 --- a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerBuilder.java +++ b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerBuilder.java @@ -3,6 +3,9 @@ package com.azure.core.serializer.json.jackson; +import com.azure.core.experimental.serializer.JsonInclusion; +import com.azure.core.experimental.serializer.JsonOptions; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; /** @@ -10,6 +13,7 @@ */ public final class JacksonJsonSerializerBuilder { private ObjectMapper objectMapper; + private JsonOptions options; /** * Constructs a new instance of {@link JacksonJsonSerializer} with the configurations set in this builder. @@ -17,9 +21,11 @@ public final class JacksonJsonSerializerBuilder { * @return A new instance of {@link JacksonJsonSerializer}. */ public JacksonJsonSerializer build() { - return (objectMapper == null) - ? new JacksonJsonSerializer(new ObjectMapper()) - : new JacksonJsonSerializer(objectMapper); + ObjectMapper mapper = objectMapper == null ? new ObjectMapper() : objectMapper; + if (options != null && options.getJsonInclusion() == JsonInclusion.ALWAYS) { + mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); + } + return new JacksonJsonSerializer(mapper); } /** @@ -34,4 +40,17 @@ public JacksonJsonSerializerBuilder serializer(ObjectMapper objectMapper) { this.objectMapper = objectMapper; return this; } + + /** + * Sets the {@link ObjectMapper} that will be used during serialization. + *

+ * If this is set to {@code null} the default {@link ObjectMapper} will be used. + * + * @param options {@link JsonOptions} that will be used during serialization. + * @return The updated JacksonJsonSerializerBuilder class. + */ + public JacksonJsonSerializerBuilder options(JsonOptions options) { + this.options = options; + return this; + } } diff --git a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerProvider.java b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerProvider.java index 6a515dce5100..a2fe299e4772 100644 --- a/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerProvider.java +++ b/sdk/core/azure-core-serializer-json-jackson/src/main/java/com/azure/core/serializer/json/jackson/JacksonJsonSerializerProvider.java @@ -3,6 +3,7 @@ package com.azure.core.serializer.json.jackson; +import com.azure.core.experimental.serializer.JsonOptions; import com.azure.core.experimental.serializer.JsonSerializer; import com.azure.core.experimental.serializer.JsonSerializerProvider; @@ -14,4 +15,9 @@ public class JacksonJsonSerializerProvider implements JsonSerializerProvider { public JsonSerializer createInstance() { return new JacksonJsonSerializerBuilder().build(); } + + @Override + public JsonSerializer createInstance(JsonOptions jsonOptions) { + return new JacksonJsonSerializerBuilder().options(jsonOptions).build(); + } } diff --git a/sdk/core/azure-core-serializer-json-jackson/src/main/java/module-info.java b/sdk/core/azure-core-serializer-json-jackson/src/main/java/module-info.java index c206672d35b2..f630fd1aa1be 100644 --- a/sdk/core/azure-core-serializer-json-jackson/src/main/java/module-info.java +++ b/sdk/core/azure-core-serializer-json-jackson/src/main/java/module-info.java @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import com.azure.core.serializer.json.jackson.JacksonJsonSerializerProvider; + module com.azure.core.serializer.json.jackson { requires transitive com.azure.core; requires transitive com.azure.core.experimental; @@ -8,5 +10,5 @@ exports com.azure.core.serializer.json.jackson; provides com.azure.core.experimental.serializer.JsonSerializerProvider - with com.azure.core.serializer.json.jackson.JacksonJsonSerializerProvider; + with JacksonJsonSerializerProvider; } diff --git a/sdk/search/azure-search-documents-serializer-gson/pom.xml b/sdk/search/azure-search-documents-serializer-gson/pom.xml new file mode 100644 index 000000000000..67a6be5bb809 --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-gson/pom.xml @@ -0,0 +1,126 @@ + + + 4.0.0 + + com.azure + azure-client-sdk-parent + 1.7.0 + ../../parents/azure-client-sdk-parent + + + com.azure + azure-search-documents-serializer-gson + jar + 1.0.0-beta.1 + + Microsoft Azure Search Gson JSON Serializer Library + This package contains the Gson JSON serializer client plugin for azure-search-documents. + https://github.com/Azure/azure-sdk-for-java + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + repo + + + + + + azure-java-build-docs + ${site.url}/site/${project.artifactId} + + + + + https://github.com/Azure/azure-sdk-for-java + scm:git:https://github.com/Azure/azure-sdk-for-java.git + scm:git:https://github.com/Azure/azure-sdk-for-java.git + + + + UTF-8 + + + + + + microsoft + Microsoft + + + + + + com.azure + azure-core-experimental + 1.0.0-beta.2 + + + com.azure + azure-core-serializer-json-gson + 1.0.0-beta.3 + + + + + org.junit.jupiter + junit-jupiter-api + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.6.2 + test + + + org.mockito + mockito-core + 3.0.0 + test + + + io.projectreactor + reactor-test + 3.3.5.RELEASE + test + + + + + + java-lts + + [11,) + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + + --add-exports com.azure.search.documents.serializer.json.jackson/com.azure.search.documents.serializer.json.jackson=com.fasterxml.jackson.databind + --add-opens com.azure.search.documents.serializer.json.jackson/com.azure.search.documents.serializer.json.jackson=ALL-UNNAMED + + + + + + + + + diff --git a/sdk/search/azure-search-documents-serializer-gson/src/main/java/com/azure/search/documents/serializer/gson/SearchGsonSerializerBuilder.java b/sdk/search/azure-search-documents-serializer-gson/src/main/java/com/azure/search/documents/serializer/gson/SearchGsonSerializerBuilder.java new file mode 100644 index 000000000000..f69c717b2ac1 --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-gson/src/main/java/com/azure/search/documents/serializer/gson/SearchGsonSerializerBuilder.java @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.search.documents.serializer.gson; + +import com.azure.core.experimental.serializer.JsonOptions; +import com.azure.core.util.serializer.JacksonAdapter; +import com.azure.search.documents.serializer.SearchSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class SearchGsonSerializerBuilder { + + /** + * Constructs a new instance of {@link SearchSerializer} with the configurations set in this builder. + * + * @return A new instance of {@link SearchSerializer}. + */ + public build() { +// return new SearchGsonSerializer(() -> { +// Gson gson = new Gson(); +// }); + return + } + + /** + * Sets the {@link ObjectMapper} that will be used during serialization. + *

+ * If this is set to {@code null} the default {@link ObjectMapper} will be used. + * + * @param jsonOptions {@link JsonOptions} that will be used during serialization. + * @return The updated JacksonJsonSerializerBuilder class. + */ + public SearchJacksonSerializerBuilder options(JsonOptions jsonOptions) { + this.options = jsonOptions; + return this; + } +} diff --git a/sdk/search/azure-search-documents-serializer-gson/src/main/java/com/azure/search/documents/serializer/gson/SearchGsonSerializerProvider.java b/sdk/search/azure-search-documents-serializer-gson/src/main/java/com/azure/search/documents/serializer/gson/SearchGsonSerializerProvider.java new file mode 100644 index 000000000000..7e8441efaa54 --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-gson/src/main/java/com/azure/search/documents/serializer/gson/SearchGsonSerializerProvider.java @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.search.documents.serializer.gson; + +import com.azure.core.experimental.serializer.JsonOptions; +import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.JsonSerializerProvider; +import com.azure.search.documents.serializer.SearchSerializer; + +public class SearchGsonSerializerProvider implements JsonSerializerProvider { + @Override + public JsonSerializer createInstance() { + return new JacksonJsonSerializerBuilder().build(); + } + + @Override + public JsonSerializer createInstance(JsonOptions jsonOptions) { + return new SearchGsonSerializerBuilder().options(jsonOptions).build(); + } +} diff --git a/sdk/search/azure-search-documents-serializer-gson/src/main/java/module-info.java b/sdk/search/azure-search-documents-serializer-gson/src/main/java/module-info.java new file mode 100644 index 000000000000..3228b9627f0e --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-gson/src/main/java/module-info.java @@ -0,0 +1,10 @@ +import com.azure.search.documents.serializer.gson.SearchGsonSerializerProvider; + +module com.azure.search.documents.serializer.gson { + requires transitive com.azure.core.experimental; + + exports com.azure.search.documents.serializer.gson; + + provides com.azure.core.experimental.serializer.JsonSerializerProvider + with SearchGsonSerializerProvider; +} diff --git a/sdk/search/azure-search-documents-serializer-gson/src/main/resources/services/com.azure.core.experimental.serializer.JsonSerializerProvider b/sdk/search/azure-search-documents-serializer-gson/src/main/resources/services/com.azure.core.experimental.serializer.JsonSerializerProvider new file mode 100644 index 000000000000..e3fb2e1be599 --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-gson/src/main/resources/services/com.azure.core.experimental.serializer.JsonSerializerProvider @@ -0,0 +1 @@ +com.azure.search.documents.serializer.jackson.SearchGsonSerializerProvider diff --git a/sdk/search/azure-search-documents-serializer-jackson/pom.xml b/sdk/search/azure-search-documents-serializer-jackson/pom.xml new file mode 100644 index 000000000000..921f178629cb --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-jackson/pom.xml @@ -0,0 +1,126 @@ + + + 4.0.0 + + com.azure + azure-client-sdk-parent + 1.7.0 + ../../parents/azure-client-sdk-parent + + + com.azure + azure-search-documents-serializer-jackson + jar + 1.0.0-beta.1 + + Microsoft Azure Search Jackson JSON Serializer Library + This package contains the Jackson JSON serializer client plugin for azure-search-documents. + https://github.com/Azure/azure-sdk-for-java + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + repo + + + + + + azure-java-build-docs + ${site.url}/site/${project.artifactId} + + + + + https://github.com/Azure/azure-sdk-for-java + scm:git:https://github.com/Azure/azure-sdk-for-java.git + scm:git:https://github.com/Azure/azure-sdk-for-java.git + + + + UTF-8 + + + + + + microsoft + Microsoft + + + + + + com.azure + azure-core-experimental + 1.0.0-beta.2 + + + com.azure + azure-core-serializer-json-jackson + 1.0.0-beta.3 + + + + + org.junit.jupiter + junit-jupiter-api + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.6.2 + test + + + org.mockito + mockito-core + 3.0.0 + test + + + io.projectreactor + reactor-test + 3.3.5.RELEASE + test + + + + + + java-lts + + [11,) + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + + --add-exports com.azure.search.documents.serializer.json.jackson/com.azure.search.documents.serializer.json.jackson=com.fasterxml.jackson.databind + --add-opens com.azure.search.documents.serializer.json.jackson/com.azure.search.documents.serializer.json.jackson=ALL-UNNAMED + + + + + + + + + diff --git a/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/SearchJacksonSerializerBuilder.java b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/SearchJacksonSerializerBuilder.java new file mode 100644 index 000000000000..5398cccebdc0 --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/SearchJacksonSerializerBuilder.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.search.documents.serializer.jackson; + +import com.azure.core.experimental.serializer.JsonOptions; +import com.azure.core.serializer.json.jackson.JacksonJsonSerializer; +import com.azure.core.serializer.json.jackson.JacksonJsonSerializerBuilder; +import com.azure.core.util.serializer.JacksonAdapter; +import com.azure.search.documents.serializer.jackson.implementation.SerializationUtil; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class SearchJacksonSerializerBuilder { + private JsonOptions options; + + /** + * Constructs a new instance of {@link JacksonJsonSerializer} with the configurations set in this builder. + * + * @return A new instance of {@link JacksonJsonSerializer}. + */ + public JacksonJsonSerializer build() { + ObjectMapper mapper = new JacksonAdapter().serializer(); + SerializationUtil.configureMapper(mapper); + return new JacksonJsonSerializerBuilder().serializer(mapper).options(options).build(); + } + + /** + * Sets the {@link ObjectMapper} that will be used during serialization. + *

+ * If this is set to {@code null} the default {@link ObjectMapper} will be used. + * + * @param jsonOptions {@link JsonOptions} that will be used during serialization. + * @return The updated JacksonJsonSerializerBuilder class. + */ + public SearchJacksonSerializerBuilder options(JsonOptions jsonOptions) { + this.options = jsonOptions; + return this; + } +} diff --git a/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/SearchJacksonSerializerProvider.java b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/SearchJacksonSerializerProvider.java new file mode 100644 index 000000000000..a7ce3046d250 --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/SearchJacksonSerializerProvider.java @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.search.documents.serializer.jackson; + +import com.azure.core.experimental.serializer.JsonOptions; +import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.JsonSerializerProvider; + +public class SearchJacksonSerializerProvider implements JsonSerializerProvider { + @Override + public JsonSerializer createInstance() { + return new SearchJacksonSerializerBuilder().build(); + } + + @Override + public JsonSerializer createInstance(JsonOptions options) { + return new SearchJacksonSerializerBuilder().options(options).build(); + } +} diff --git a/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/GeoPointDeserializer.java b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/GeoPointDeserializer.java new file mode 100644 index 000000000000..3f6c8371b210 --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/GeoPointDeserializer.java @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.search.documents.serializer.jackson.implementation; + +import com.azure.core.experimental.spatial.GeometryPosition; +import com.azure.core.experimental.spatial.PointGeometry; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Custom deserializer to detect GeoJSON structures in dynamic results and deserialize as instances of + * {@link PointGeometry} + */ +final class GeoPointDeserializer extends UntypedObjectDeserializer { + private static final long serialVersionUID = 1L; + private final UntypedObjectDeserializer defaultDeserializer; + + /** + * Constructor + * + * @param defaultDeserializer the deserializer to use when a GeoJSON match is not found + */ + GeoPointDeserializer(UntypedObjectDeserializer defaultDeserializer) { + super(null, null); + this.defaultDeserializer = defaultDeserializer; + } + + @Override + public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + if (jp.currentTokenId() == JsonTokenId.ID_START_OBJECT) { + Object obj = defaultDeserializer.deserialize(jp, ctxt); + return parseGeoPoint(obj); + } else if (jp.currentTokenId() == JsonTokenId.ID_START_ARRAY) { + List list = (List) defaultDeserializer.deserialize(jp, ctxt); + return list.stream() + .map(this::parseGeoPoint) + .collect(Collectors.toList()); + } else { + return defaultDeserializer.deserialize(jp, ctxt); + } + } + + /** + * Converts an object to a GeoPoint if it is valid GeoJSON, otherwise returns the original object. + * + * @param obj the object to parse + * @return an instance of {@link PointGeometry} if valid GeoJSON, otherwise obj. + */ + @SuppressWarnings("unchecked") + private Object parseGeoPoint(Object obj) { + if (isGeoJsonPoint(obj)) { + // We already know the class is a Map - validated in isGeoJsonPoint. + Map map = (Map) obj; + List coordinates = (List) map.get("coordinates"); + + Double latitude = coordinates.get(1).getClass() == Double.class + ? (Double) coordinates.get(1) + : Double.valueOf((Integer) coordinates.get(1)); + Double longitude = coordinates.get(0).getClass() == Double.class + ? (Double) coordinates.get(0) + : Double.valueOf((Integer) coordinates.get(0)); + + return new PointGeometry(new GeometryPosition(longitude, latitude)); + } else { + return obj; + } + } + + /** + * Determines whether an object is valid GeoJSON object. + * + * @param obj the object to test + * @return true if the object is valid GeoJSON, false otherwise. + */ + @SuppressWarnings("unchecked") + private boolean isGeoJsonPoint(Object obj) { + try { + if (obj instanceof Map) { + Map map = (Map) obj; + + return isValidPoint(map) + && isValidCoordinates(map) + && isValidCrs(map); + } + + return false; + } catch (RuntimeException ex) { + // somehow we got an object which isn't a Map + return false; + } + } + + private boolean isValidPoint(Map map) { + if (map != null && map.containsKey("type")) { + return map.get("type").equals("Point"); + } + + return false; + } + + @SuppressWarnings("unchecked") + private boolean isValidCrs(Map map) { + // crs is not required to deserialize, but must be valid if present + boolean isValidCrs; + if (map != null && map.containsKey("crs")) { + Map crs = (Map) map.get("crs"); + boolean isValidType = crs.get("type").equals("name"); + + Map properties = (Map) crs.get("properties"); + boolean isValidProperties = properties.get("name").equals("EPSG:4326"); + + isValidCrs = isValidType && isValidProperties; + } else { + isValidCrs = true; + } + return isValidCrs; + } + + private boolean isValidCoordinates(Map map) { + boolean isValidCoordinates = false; + List coordinates = (List) map.get("coordinates"); + if (coordinates.size() == 2) { + Class longitudeClass = coordinates.get(0).getClass(); + boolean isValidLongitude = longitudeClass == Integer.class + || longitudeClass == Double.class; + + Class latitudeClass = coordinates.get(1).getClass(); + boolean isValidLatitude = latitudeClass == Integer.class + || latitudeClass == Double.class; + + isValidCoordinates = isValidLongitude && isValidLatitude; + } + return isValidCoordinates; + } +} diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/Iso8601DateDeserializer.java b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/Iso8601DateDeserializer.java similarity index 92% rename from sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/Iso8601DateDeserializer.java rename to sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/Iso8601DateDeserializer.java index 3d857d3825a5..d3b150ced76d 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/Iso8601DateDeserializer.java +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/Iso8601DateDeserializer.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.search.documents.implementation; +package com.azure.search.documents.serializer.jackson.implementation; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonTokenId; @@ -14,7 +14,7 @@ import java.util.List; import java.util.stream.Collectors; -public class Iso8601DateDeserializer extends UntypedObjectDeserializer { +class Iso8601DateDeserializer extends UntypedObjectDeserializer { private static final long serialVersionUID = 1L; private final UntypedObjectDeserializer defaultDeserializer; private static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/Iso8601DateSerializer.java b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/Iso8601DateSerializer.java similarity index 92% rename from sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/Iso8601DateSerializer.java rename to sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/Iso8601DateSerializer.java index 220df9aa6476..c8f259b58d19 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/Iso8601DateSerializer.java +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/Iso8601DateSerializer.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.search.documents.implementation; +package com.azure.search.documents.serializer.jackson.implementation; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; @@ -16,7 +16,7 @@ /** * Custom serializer to serialize {@link java.util.Date} to Iso8601 standard date format "yyyy-MM-dd'T'hh:mm:ss.SSS'Z'". */ -final class Iso8601DateSerializer extends JsonSerializer { +public final class Iso8601DateSerializer extends JsonSerializer { /** * Gets a module wrapping this serializer as an adapter for the Jackson diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/SerializationUtil.java b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/SerializationUtil.java similarity index 68% rename from sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/SerializationUtil.java rename to sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/SerializationUtil.java index e4b780d8df9c..0b28bb700396 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/SerializationUtil.java +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/com/azure/search/documents/serializer/jackson/implementation/SerializationUtil.java @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.search.documents.implementation; +package com.azure.search.documents.serializer.jackson.implementation; +import com.azure.core.experimental.spatial.GeometryDeserializer; +import com.azure.core.experimental.spatial.GeometrySerializer; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer; @@ -14,15 +16,20 @@ public class SerializationUtil { /** * Configures an {@link ObjectMapper} with custom behavior needed to work with the Azure Cognitive Search REST API. + * @param mapper The Jackson ObjectMapper. */ public static void configureMapper(ObjectMapper mapper) { mapper.registerModule(new JavaTimeModule()); mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); UntypedObjectDeserializer defaultDeserializer = new UntypedObjectDeserializer(null, null); Iso8601DateDeserializer iso8601DateDeserializer = new Iso8601DateDeserializer(defaultDeserializer); + GeoPointDeserializer geoPointDeserializer = new GeoPointDeserializer(iso8601DateDeserializer); SimpleModule module = new SimpleModule(); - module.addDeserializer(Object.class, iso8601DateDeserializer); + module.addDeserializer(Object.class, geoPointDeserializer); mapper.registerModule(Iso8601DateSerializer.getModule()); + mapper.registerModule(GeometrySerializer.getModule()); + mapper.registerModule(GeometryDeserializer.getModule()); + mapper.registerModule(module); } } diff --git a/sdk/search/azure-search-documents-serializer-jackson/src/main/java/module-info.java b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/module-info.java new file mode 100644 index 000000000000..9d379bb5231d --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/java/module-info.java @@ -0,0 +1,11 @@ +import com.azure.search.documents.serializer.jackson.SearchJacksonSerializerProvider; + +module com.azure.search.documents.serializer.jackson { + requires com.azure.core.experimental; + requires com.azure.core.serializer.json.jackson; + + exports com.azure.search.documents.serializer.jackson; + + provides com.azure.core.experimental.serializer.JsonSerializerProvider + with SearchJacksonSerializerProvider; +} diff --git a/sdk/search/azure-search-documents-serializer-jackson/src/main/resources/META-INF/services/com.azure.core.experimental.serializer.JsonSerializerProvider b/sdk/search/azure-search-documents-serializer-jackson/src/main/resources/META-INF/services/com.azure.core.experimental.serializer.JsonSerializerProvider new file mode 100644 index 000000000000..66c7a620192a --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-jackson/src/main/resources/META-INF/services/com.azure.core.experimental.serializer.JsonSerializerProvider @@ -0,0 +1 @@ +com.azure.search.documents.serializer.jackson.SearchJacksonSerializerProvider diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/Iso8601DeserializerTests.java b/sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/Iso8601DeserializerTests.java similarity index 93% rename from sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/Iso8601DeserializerTests.java rename to sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/Iso8601DeserializerTests.java index 21bae94f5f07..0fee82d5c9f2 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/Iso8601DeserializerTests.java +++ b/sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/Iso8601DeserializerTests.java @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.search.documents.implementation; +package com.azure.search.documents.serializer.implementation; +import com.azure.search.documents.serializer.jackson.implementation.Iso8601DateSerializer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -17,7 +18,6 @@ import java.util.Map; import java.util.TimeZone; -import static com.azure.search.documents.TestHelpers.assertDateEquals; import static org.junit.jupiter.api.Assertions.assertEquals; public class Iso8601DeserializerTests { @@ -70,8 +70,8 @@ public void deserializesListOfOffsetDateTime() throws Exception { Date actual1 = obj.get(0); Date actual2 = obj.get(1); - assertDateEquals(expected1, actual1); - assertDateEquals(expected2, actual2); + TestHelpers.assertDateEquals(expected1, actual1); + TestHelpers.assertDateEquals(expected2, actual2); } @Test diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/Iso8601SerializerTests.java b/sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/Iso8601SerializerTests.java similarity index 92% rename from sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/Iso8601SerializerTests.java rename to sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/Iso8601SerializerTests.java index 23dcfa569459..84b799925165 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/Iso8601SerializerTests.java +++ b/sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/Iso8601SerializerTests.java @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.search.documents.implementation; +package com.azure.search.documents.serializer.implementation; +import com.azure.search.documents.serializer.jackson.implementation.Iso8601DateSerializer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; diff --git a/sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/TestHelpers.java b/sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/TestHelpers.java new file mode 100644 index 000000000000..69101d66092c --- /dev/null +++ b/sdk/search/azure-search-documents-serializer-jackson/src/test/java/com/azure/search/documents/serializer/implementation/TestHelpers.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.search.documents.serializer.implementation; + +import java.time.ZoneOffset; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestHelpers { + private TestHelpers() { + } + + public static void assertDateEquals(Date expect, Date actual) { + assertEquals(0, expect.toInstant().atOffset(ZoneOffset.UTC) + .compareTo(actual.toInstant().atOffset(ZoneOffset.UTC))); + } +} diff --git a/sdk/search/azure-search-documents/pom.xml b/sdk/search/azure-search-documents/pom.xml index 9f8f1fd8add8..72cd2ef09795 100644 --- a/sdk/search/azure-search-documents/pom.xml +++ b/sdk/search/azure-search-documents/pom.xml @@ -35,6 +35,11 @@ azure-core-http-netty 1.5.3 + + com.azure + azure-core-experimental + 1.0.0-beta.2 + test + + com.azure + azure-search-documents-serializer-jackson + 1.0.0-beta.1 + test + org.junit.jupiter junit-jupiter-api diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchAsyncClient.java b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchAsyncClient.java index 4320fc63a350..661283c6901d 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchAsyncClient.java +++ b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/SearchAsyncClient.java @@ -6,16 +6,19 @@ import com.azure.core.annotation.ReturnType; import com.azure.core.annotation.ServiceClient; import com.azure.core.annotation.ServiceMethod; +import com.azure.core.experimental.serializer.JsonInclusion; +import com.azure.core.experimental.serializer.JsonOptions; +import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.JsonSerializerProviders; +import com.azure.core.experimental.serializer.Type; import com.azure.core.http.HttpPipeline; import com.azure.core.http.rest.Response; import com.azure.core.http.rest.SimpleResponse; import com.azure.core.util.Context; import com.azure.core.util.ServiceVersion; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.serializer.JacksonAdapter; import com.azure.search.documents.implementation.SearchIndexClientImpl; import com.azure.search.documents.implementation.SearchIndexClientImplBuilder; -import com.azure.search.documents.implementation.SerializationUtil; import com.azure.search.documents.implementation.converters.AutocompleteModeConverter; import com.azure.search.documents.implementation.converters.FacetResultConverter; import com.azure.search.documents.implementation.converters.IndexBatchBaseConverter; @@ -53,9 +56,6 @@ import com.azure.search.documents.util.SearchPagedResponse; import com.azure.search.documents.util.SuggestPagedFlux; import com.azure.search.documents.util.SuggestPagedResponse; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import reactor.core.publisher.Mono; import java.util.ArrayList; @@ -111,20 +111,13 @@ public final class SearchAsyncClient { */ private final HttpPipeline httpPipeline; - private static final ObjectMapper MAPPER; - - static { - MAPPER = new JacksonAdapter().serializer(); - SerializationUtil.configureMapper(MAPPER); - MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS); - } + private static final JsonSerializer SERIALIZER = createSerializerInstance(); /** * Package private constructor to be used by {@link SearchClientBuilder} */ SearchAsyncClient(String endpoint, String indexName, SearchServiceVersion serviceVersion, HttpPipeline httpPipeline) { - this.endpoint = endpoint; this.indexName = indexName; this.serviceVersion = serviceVersion; @@ -536,15 +529,14 @@ Mono> getDocumentWithResponse(String key, Class modelClass, L return restClient.getDocuments() .getWithResponseAsync(key, selectedFields, null, context) .onErrorMap(DocumentResponseConversions::exceptionMapper) - .map(res -> { + .flatMap(res -> { if (SearchDocument.class == modelClass) { - TypeReference> typeReference = new TypeReference>() { - }; - SearchDocument doc = new SearchDocument(MAPPER.convertValue(res.getValue(), typeReference)); - return new SimpleResponse(res, (T) doc); + Type> typeReference = new Type>() {}; + return SERIALIZER.convertValue(res.getValue(), typeReference) + .map(doc -> new SimpleResponse(res, (T) new SearchDocument(doc))); } - T document = MAPPER.convertValue(res.getValue(), modelClass); - return new SimpleResponse<>(res, document); + return SERIALIZER.convertValue(res.getValue(), modelClass) + .map(document -> new SimpleResponse<>(res, document)); }) .map(Function.identity()); } catch (RuntimeException ex) { @@ -957,4 +949,8 @@ private static IndexDocumentsBatch buildIndexBatch(Iterable documents, return new IndexDocumentsBatch().addActions(actions); } + + private static JsonSerializer createSerializerInstance() { + return JsonSerializerProviders.createInstance(new JsonOptions().setJsonInclusion(JsonInclusion.ALWAYS)); + } } diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/converters/IndexActionConverter.java b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/converters/IndexActionConverter.java index f5a6d765975e..7a7e72e9d1cc 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/converters/IndexActionConverter.java +++ b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/implementation/converters/IndexActionConverter.java @@ -3,14 +3,14 @@ package com.azure.search.documents.implementation.converters; -import com.azure.core.util.serializer.JacksonAdapter; -import com.azure.search.documents.implementation.SerializationUtil; +import com.azure.core.experimental.serializer.JsonInclusion; +import com.azure.core.experimental.serializer.JsonOptions; +import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.JsonSerializerProviders; +import com.azure.core.experimental.serializer.Type; import com.azure.search.documents.implementation.util.PrivateFieldAccessHelper; import com.azure.search.documents.models.IndexAction; import com.azure.search.documents.models.IndexActionType; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; @@ -18,15 +18,9 @@ * A converter between {@link com.azure.search.documents.implementation.models.IndexAction} and {@link IndexAction}. */ public final class IndexActionConverter { - private static final ObjectMapper DYNAMIC_TYPE_MAPPER; - private static final ObjectMapper STRONGLY_TYPE_MAPPER; - static { - DYNAMIC_TYPE_MAPPER = new JacksonAdapter().serializer(); - STRONGLY_TYPE_MAPPER = new JacksonAdapter().serializer(); - SerializationUtil.configureMapper(DYNAMIC_TYPE_MAPPER); - SerializationUtil.configureMapper(STRONGLY_TYPE_MAPPER); - } + private static final JsonSerializer SERIALIZER = JsonSerializerProviders.createInstance( + new JsonOptions().setJsonInclusion(JsonInclusion.ALWAYS)); /** * Maps from {@link com.azure.search.documents.implementation.models.IndexAction} to {@link IndexAction}. @@ -67,15 +61,15 @@ public static com.azure.search.documents.implementation.models.IndexAction m } Map additionalProperties; - TypeReference> typeRef = new TypeReference>() {}; + Type> typeRef = new Type>() {}; Map mapProperties = PrivateFieldAccessHelper.get(obj, "properties", Map.class); if (mapProperties != null) { - DYNAMIC_TYPE_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS); - additionalProperties = DYNAMIC_TYPE_MAPPER.convertValue(mapProperties, typeRef); + additionalProperties = SERIALIZER.convertValue(mapProperties, typeRef).block(); } else { T properties = obj.getDocument(); - additionalProperties = STRONGLY_TYPE_MAPPER.convertValue(properties, typeRef); + JsonSerializer searchSerializer = JsonSerializerProviders.createInstance(); + additionalProperties = searchSerializer.convertValue(properties, typeRef).block(); } indexAction.setAdditionalProperties(additionalProperties); diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/ScoringParameter.java b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/ScoringParameter.java index 0c62a4c4b24e..7060a57ed88b 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/ScoringParameter.java +++ b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/ScoringParameter.java @@ -3,6 +3,7 @@ package com.azure.search.documents.models; +import com.azure.core.experimental.spatial.PointGeometry; import com.azure.core.util.CoreUtils; import com.azure.core.util.logging.ClientLogger; import com.fasterxml.jackson.annotation.JsonCreator; @@ -60,15 +61,15 @@ public ScoringParameter(@JsonProperty(value = "name") String name, this.values = new ArrayList<>(values); } -// /** -// * Initializes a new instance of the ScoringParameter class with the given name and GeographyPoint value. -// * -// * @param name Name of the scoring parameter. -// * @param value Value of the scoring parameter. -// */ -// public ScoringParameter(String name, PointGeometry value) { -// this(name, toLonLatStrings(value)); -// } + /** + * Initializes a new instance of the ScoringParameter class with the given name and GeographyPoint value. + * + * @param name Name of the scoring parameter. + * @param value Value of the scoring parameter. + */ + public ScoringParameter(String name, PointGeometry value) { + this(name, toLonLatStrings(value)); + } /** * Gets the name of the scoring parameter. @@ -88,11 +89,11 @@ public List getValues() { return new ArrayList<>(values); } -// private static List toLonLatStrings(PointGeometry point) { -// Objects.requireNonNull(point); -// return Arrays.asList(String.valueOf(point.getPosition().getLongitude()), -// String.valueOf(point.getPosition().getLatitude())); -// } + private static List toLonLatStrings(PointGeometry point) { + Objects.requireNonNull(point); + return Arrays.asList(String.valueOf(point.getPosition().getLongitude()), + String.valueOf(point.getPosition().getLatitude())); + } /** * Covert {@link ScoringParameter} to string. diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SearchResult.java b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SearchResult.java index 5ff4660a05ce..0ada96d0d6da 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SearchResult.java +++ b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SearchResult.java @@ -4,13 +4,11 @@ package com.azure.search.documents.models; import com.azure.core.annotation.Fluent; -import com.azure.core.util.serializer.JacksonAdapter; +import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.JsonSerializerProviders; import com.azure.search.documents.SearchDocument; -import com.azure.search.documents.implementation.SerializationUtil; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; import java.util.Map; @@ -20,12 +18,7 @@ */ @Fluent public final class SearchResult { - private static final ObjectMapper MAPPER; - static { - MAPPER = new JacksonAdapter().serializer(); - SerializationUtil.configureMapper(MAPPER); - MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS); - } + private static final JsonSerializer SERIALIZER = JsonSerializerProviders.createInstance(); /* * Unmatched properties from the message are deserialized this collection @@ -69,7 +62,7 @@ public SearchResult( * @return the additionalProperties value. */ public T getDocument(Class modelClass) { - return MAPPER.convertValue(this.additionalProperties, modelClass); + return SERIALIZER.convertValue(this.additionalProperties, modelClass).block(); } /** diff --git a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SuggestResult.java b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SuggestResult.java index 685ac4ae1e0c..4c020ecb8e7e 100644 --- a/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SuggestResult.java +++ b/sdk/search/azure-search-documents/src/main/java/com/azure/search/documents/models/SuggestResult.java @@ -2,14 +2,13 @@ // Licensed under the MIT License. package com.azure.search.documents.models; + import com.azure.core.annotation.Fluent; -import com.azure.core.util.serializer.JacksonAdapter; +import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.JsonSerializerProviders; import com.azure.search.documents.SearchDocument; -import com.azure.search.documents.implementation.SerializationUtil; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; /** * A result containing a document found by a suggestion query, plus associated @@ -17,12 +16,7 @@ */ @Fluent public final class SuggestResult { - private static final ObjectMapper MAPPER; - static { - MAPPER = new JacksonAdapter().serializer(); - SerializationUtil.configureMapper(MAPPER); - MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS); - } + private static final JsonSerializer SERIALIZER = JsonSerializerProviders.createInstance(); /* * Unmatched properties from the message are deserialized this collection @@ -57,7 +51,7 @@ public SuggestResult( * @return the additionalProperties value. */ public T getDocument(Class modelClass) { - return MAPPER.convertValue(this.additionalProperties, modelClass); + return SERIALIZER.convertValue(this.additionalProperties, modelClass).block(); } /** diff --git a/sdk/search/azure-search-documents/src/main/java/module-info.java b/sdk/search/azure-search-documents/src/main/java/module-info.java index 44e0917971da..9475809c7164 100644 --- a/sdk/search/azure-search-documents/src/main/java/module-info.java +++ b/sdk/search/azure-search-documents/src/main/java/module-info.java @@ -5,6 +5,7 @@ requires transitive com.azure.core; requires com.fasterxml.jackson.datatype.jsr310; requires jakarta.activation; + requires com.azure.core.experimental; opens com.azure.search.documents to com.fasterxml.jackson.databind; opens com.azure.search.documents.models to com.fasterxml.jackson.databind; diff --git a/sdk/search/azure-search-documents/src/samples/java/com/azure/search/documents/PointGeometry.java b/sdk/search/azure-search-documents/src/samples/java/com/azure/search/documents/PointGeometry.java deleted file mode 100644 index ee7dca8b9d9b..000000000000 --- a/sdk/search/azure-search-documents/src/samples/java/com/azure/search/documents/PointGeometry.java +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.search.documents; - -import java.util.Map; -import java.util.Objects; - -/** - * Represents a geometric point. - */ -public final class PointGeometry { - private final GeometryBoundingBox boundingBox; - private final Map properties; - - private final GeometryPosition position; - - /** - * Constructs a geometric point. - * - * @param position The {@link GeometryPosition geometric position} of the point. - * @throws NullPointerException If {@code position} is {@code null}. - */ - public PointGeometry(GeometryPosition position) { - this(position, null, null); - } - - /** - * Constructs a geometric point. - * - * @param position The {@link GeometryPosition geometric position} of the point. - * @param boundingBox Bounding box for the point. - * @param properties Additional properties of the geometric point. - * @throws NullPointerException If {@code position} is {@code null}. - */ - public PointGeometry(GeometryPosition position, GeometryBoundingBox boundingBox, Map properties) { - Objects.requireNonNull(position, "'position' cannot be null."); - - this.boundingBox = boundingBox; - this.properties = properties; - this.position = position; - } - - /** - * The {@link GeometryPosition geometric position} of the point. - * - * @return The {@link GeometryPosition geometric position} of the point. - */ - public GeometryPosition getPosition() { - return position; - } - - /** - * Bounding box for this geometry. - * - * @return The bounding box for this geometry. - */ - public GeometryBoundingBox getBoundingBox() { - return boundingBox; - } - - /** - * Additional properties about this geometry. - * - * @return An unmodifiable representation of the additional properties associated with this geometry. - */ - public Map getProperties() { - return properties; - } - - @Override - public int hashCode() { - return Objects.hash(position, super.hashCode()); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PointGeometry)) { - return false; - } - - if (this == obj) { - return true; - } - - PointGeometry other = (PointGeometry) obj; - - return Objects.equals(boundingBox, other.boundingBox) - && Objects.equals(properties, other.properties) - && Objects.equals(position, other.position); - } -} diff --git a/sdk/search/azure-search-documents/src/samples/java/com/azure/search/documents/indexes/FieldBuilder.java b/sdk/search/azure-search-documents/src/samples/java/com/azure/search/documents/indexes/FieldBuilder.java index b8be808d10ce..67f66f6adc0b 100644 --- a/sdk/search/azure-search-documents/src/samples/java/com/azure/search/documents/indexes/FieldBuilder.java +++ b/sdk/search/azure-search-documents/src/samples/java/com/azure/search/documents/indexes/FieldBuilder.java @@ -3,6 +3,7 @@ package com.azure.search.documents.indexes; +import com.azure.core.experimental.spatial.PointGeometry; import com.azure.core.util.logging.ClientLogger; import com.azure.search.documents.indexes.models.LexicalAnalyzerName; import com.azure.search.documents.indexes.models.SearchField; @@ -38,7 +39,7 @@ public final class FieldBuilder { SUPPORTED_NONE_PARAMETERIZED_TYPE.put(String.class, SearchFieldDataType.STRING); SUPPORTED_NONE_PARAMETERIZED_TYPE.put(Date.class, SearchFieldDataType.DATE_TIME_OFFSET); SUPPORTED_NONE_PARAMETERIZED_TYPE.put(OffsetDateTime.class, SearchFieldDataType.DATE_TIME_OFFSET); -// SUPPORTED_NONE_PARAMETERIZED_TYPE.put(PointGeometry.class, SearchFieldDataType.GEOGRAPHY_POINT); + SUPPORTED_NONE_PARAMETERIZED_TYPE.put(PointGeometry.class, SearchFieldDataType.GEOGRAPHY_POINT); } private static final List> UNSUPPORTED_TYPES = Arrays.asList(Byte.class, diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/IndexingSyncTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/IndexingSyncTests.java index 0659cf655c69..181dd2669f57 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/IndexingSyncTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/IndexingSyncTests.java @@ -46,6 +46,7 @@ import static com.azure.search.documents.TestHelpers.assertHttpResponseException; import static com.azure.search.documents.TestHelpers.assertMapEquals; import static com.azure.search.documents.TestHelpers.assertObjectEquals; +import static com.azure.search.documents.TestHelpers.createPointGeometry; import static com.azure.search.documents.TestHelpers.waitForIndexing; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -456,7 +457,7 @@ public void canMergeStaticallyTypedDocuments() throws ParseException { .smokingAllowed(true) .lastRenovationDate(dateFormat.parse("2010-06-27T00:00:00Z")) .rating(4) -// .location(createPointGeometryString(40.760586, -73.975403)) + .location(createPointGeometry(40.760586, -73.975403)) .address(new HotelAddress() .streetAddress("677 5th Ave") .city("New York") @@ -494,7 +495,7 @@ public void canMergeStaticallyTypedDocuments() throws ParseException { .parkingIncluded(true) .lastRenovationDate(null) .rating(3) -// .location(null) + .location(null) .address(new HotelAddress()) .rooms(Collections.singletonList( new HotelRoom() @@ -518,7 +519,7 @@ public void canMergeStaticallyTypedDocuments() throws ParseException { .smokingAllowed(true) .lastRenovationDate(dateFormat.parse("2010-06-27T00:00:00Z")) .rating(3) -// .location(createPointGeometryString(40.760586, -73.975403)) + .location(createPointGeometry(40.760586, -73.975403)) .address(new HotelAddress() .streetAddress("677 5th Ave") .city("New York") @@ -584,7 +585,7 @@ public void canSetExplicitNullsInStaticallyTypedDocument() throws ParseException .SMOKINGALLOWED(false) .LASTRENOVATIONDATE(dateFormat.parse("1970-01-18T05:00:00Z")) .RATING(4) -// .LOCATION(createPointGeometryString(40.760586, -73.975403)) + .LOCATION(createPointGeometry(40.760586, -73.975403)) .ADDRESS(new HotelAddress() .streetAddress("677 5th Ave") .city("New York") @@ -620,7 +621,7 @@ public void canSetExplicitNullsInStaticallyTypedDocument() throws ParseException .PARKINGINCLUDED(true) .LASTRENOVATIONDATE(dateFormat.parse("1970-01-18T05:00:00Z")) .RATING(3) -// .LOCATION(null) // This property has JsonInclude.Include.ALWAYS, so this will null out the field. + .LOCATION(null) // This property has JsonInclude.Include.ALWAYS, so this will null out the field. .ADDRESS(new HotelAddress()) .ROOMS(Collections.singletonList( new HotelRoom() @@ -642,7 +643,7 @@ public void canSetExplicitNullsInStaticallyTypedDocument() throws ParseException .SMOKINGALLOWED(false) .LASTRENOVATIONDATE(dateFormat.parse("1970-01-18T05:00:00Z")) .RATING(3) -// .LOCATION(null) + .LOCATION(null) .ADDRESS(new HotelAddress() .streetAddress("677 5th Ave") .city("New York") @@ -698,7 +699,7 @@ public void canMergeDynamicDocuments() { originalDoc.put("SmokingAllowed", true); originalDoc.put("LastRenovationDate", OffsetDateTime.parse("2010-06-27T00:00:00Z")); originalDoc.put("Rating", 4); -// originalDoc.put("Location", createPointGeometry(40.760586, -73.965403)); + originalDoc.put("Location", createPointGeometry(40.760586, -73.965403)); SearchDocument originalAddress = new SearchDocument(); originalAddress.put("StreetAddress", "677 5th Ave"); @@ -907,7 +908,7 @@ Hotel prepareStaticallyTypedHotel(String hotelId) throws ParseException { .smokingAllowed(false) .lastRenovationDate(dateFormat.parse("2010-06-27T00:00:00Z")) .rating(5) -// .location(createPointGeometryString(47.678581, -122.131577)) + .location(createPointGeometry(47.678581, -122.131577)) .address( new HotelAddress() .streetAddress("1 Microsoft Way") @@ -1003,7 +1004,7 @@ List getBoundaryValues() { .category("") .lastRenovationDate(new Date(minEpoch.getYear(), minEpoch.getMonth(), minEpoch.getDate(), minEpoch.getHours(), minEpoch.getMinutes(), minEpoch.getSeconds())) -// .location(createPointGeometryString(-90.0, -180.0)) // South pole, date line from the west + .location(createPointGeometry(-90.0, -180.0)) // South pole, date line from the west .parkingIncluded(false) .rating(Integer.MIN_VALUE) .tags(Collections.emptyList()) @@ -1018,7 +1019,7 @@ List getBoundaryValues() { .category("test") // No meaningful string max since there is no length limit (other than payload size or term length). .lastRenovationDate(new Date(maxEpoch.getYear(), maxEpoch.getMonth(), maxEpoch.getDate(), maxEpoch.getHours(), maxEpoch.getMinutes(), maxEpoch.getSeconds())) -// .location(createPointGeometryString(90.0, 180.0)) // North pole, date line from the east + .location(createPointGeometry(90.0, 180.0)) // North pole, date line from the east .parkingIncluded(true) .rating(Integer.MAX_VALUE) .tags(Collections.singletonList("test")) // No meaningful string max; see above. @@ -1033,7 +1034,7 @@ List getBoundaryValues() { .hotelId("3") .category(null) .lastRenovationDate(null) -// .location(createPointGeometryString(0.0, 0.0)) // Equator, meridian + .location(createPointGeometry(0.0, 0.0)) // Equator, meridian .parkingIncluded(null) .rating(null) .tags(Collections.emptyList()) @@ -1046,7 +1047,7 @@ List getBoundaryValues() { // Other boundary values #2 new Hotel() .hotelId("4") -// .location(null) + .location(null) .tags(Collections.emptyList()) .rooms(Collections.singletonList( new HotelRoom() diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/LookupSyncTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/LookupSyncTests.java index 4d4838b7b99d..f67c9bd815cc 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/LookupSyncTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/LookupSyncTests.java @@ -2,6 +2,7 @@ // Licensed under the MIT License. package com.azure.search.documents; +import com.azure.core.experimental.spatial.PointGeometry; import com.azure.core.http.rest.Response; import com.azure.core.util.Context; import com.azure.search.documents.indexes.SearchIndexClient; @@ -30,6 +31,7 @@ import static com.azure.search.documents.TestHelpers.assertMapEquals; import static com.azure.search.documents.TestHelpers.assertObjectEquals; +import static com.azure.search.documents.TestHelpers.createPointGeometry; import static com.azure.search.documents.TestHelpers.uploadDocument; import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NaN; @@ -269,14 +271,14 @@ public void getDynamicDocumentCannotAlwaysDetermineCorrectType() { SearchDocument indexedDoc = new SearchDocument(); indexedDoc.put("HotelId", "1"); indexedDoc.put("LastRenovationDate", "2017-01-13T14:03:00.7552052-07:00"); -// indexedDoc.put("Location", createPointGeometryString(40.760586, -73.975403)); // Test that we don't confuse Geo-JSON & complex types. + indexedDoc.put("Location", createPointGeometry(40.760586, -73.975403)); // Test that we don't confuse Geo-JSON & complex types. indexedDoc.put("Rooms", Collections.singletonList(new SearchDocument(Collections.singletonMap("BaseRate", NaN)))); SearchDocument expectedDoc = new SearchDocument(); expectedDoc.put("HotelId", "1"); expectedDoc.put("LastRenovationDate", OffsetDateTime.of(2017, 1, 13, 21, 3, 0, 755000000, ZoneOffset.UTC)); -// expectedDoc.put("Location", createPointGeometryString(40.760586, -73.975403)); + expectedDoc.put("Location", createPointGeometry(40.760586, -73.975403)); expectedDoc.put("Rooms", Collections.singletonList(new SearchDocument(Collections.singletonMap("BaseRate", "NaN")))); client.indexDocuments(new IndexDocumentsBatch<>().addUploadActions(Collections.singletonList(indexedDoc))); @@ -284,7 +286,7 @@ public void getDynamicDocumentCannotAlwaysDetermineCorrectType() { // Select only the fields set in the test case so we don't get superfluous data back. List selectedFields = Arrays.asList("HotelId", "LastRenovationDate", "Location", "Rooms/BaseRate"); assertMapEquals(expectedDoc, client.getDocumentWithResponse("1", SearchDocument.class, selectedFields, - Context.NONE).getValue(), false, "properties"); + Context.NONE).getValue(), true, "boundingBox", "properties"); } @Test @@ -360,7 +362,7 @@ public void dynamicallyTypedPrimitiveCollectionsDoNotAllRoundtripCorrectly() { String docKey = "1"; OffsetDateTime dateTime = OffsetDateTime.parse("2019-08-13T14:30:00Z"); -// String geoPoint = createPointGeometryString(1.0, 100.0); + PointGeometry geoPoint = createPointGeometry(1.0, 100.0); SearchDocument indexedDoc = new SearchDocument(); indexedDoc.put("Key", docKey); @@ -370,7 +372,7 @@ public void dynamicallyTypedPrimitiveCollectionsDoNotAllRoundtripCorrectly() { indexedDoc.put("Longs", new Long[]{9999999999999999L, 832372345832523L}); indexedDoc.put("Strings", new String[]{"hello", "bye"}); indexedDoc.put("Ints", new int[]{1, 2, 3, 4, -13, 5, 0}); -// indexedDoc.put("Points", new String[]{geoPoint}); + indexedDoc.put("Points", new PointGeometry[]{geoPoint}); // This is the expected document when querying the document later SearchDocument expectedDoc = new SearchDocument(); @@ -380,7 +382,7 @@ public void dynamicallyTypedPrimitiveCollectionsDoNotAllRoundtripCorrectly() { expectedDoc.put("Longs", Arrays.asList(9999999999999999L, 832372345832523L)); expectedDoc.put("Strings", Arrays.asList("hello", "bye")); expectedDoc.put("Ints", Arrays.asList(1, 2, 3, 4, -13, 5, 0)); -// expectedDoc.put("Points", Collections.singletonList(geoPoint)); + expectedDoc.put("Points", Collections.singletonList(geoPoint)); expectedDoc.put("Dates", Collections.singletonList(dateTime)); uploadDocument(client, indexedDoc); @@ -407,7 +409,7 @@ Hotel prepareExpectedHotel() { expectDate.getMonth(), expectDate.getDate(), expectDate.getHours(), expectDate.getMinutes(), expectDate.getSeconds())) .rating(5) -// .location(createPointGeometryString(47.678581, -122.131577)) + .location(createPointGeometry(47.678581, -122.131577)) .rooms(new ArrayList<>()); } @@ -435,7 +437,7 @@ Hotel prepareSelectedFieldsHotel() { .smokingAllowed(true) .lastRenovationDate(new Date(2010 - 1900, Calendar.JUNE, 26, 13, 0, 0)) .rating(3) -// .location(createPointGeometryString(35.904160, -78.940483)) + .location(createPointGeometry(35.904160, -78.940483)) .address(new HotelAddress().streetAddress("6910 Fayetteville Rd").city("Durham").stateProvince("NC").country("USA").postalCode("27713")) .rooms(Arrays.asList( new HotelRoom() @@ -468,9 +470,9 @@ ModelWithPrimitiveCollections preparePrimitivesModel() { .doubles(new Double[]{NEGATIVE_INFINITY, 0.0, 2.78, NaN, 3.14, POSITIVE_INFINITY}) .ints(new int[]{1, 2, 3, 4, -13, 5, 0}) .longs(new Long[]{-9_999_999_999_999_999L, 832_372_345_832_523L}) -// .points(new String[]{ -// createPointGeometryString(49.0, -67.0), -// createPointGeometryString(47.0, 21.0)}) + .points(new PointGeometry[]{ + createPointGeometry(49.0, -67.0), + createPointGeometry(47.0, 21.0)}) .strings(new String[]{"hello", "2019-04-14T14:56:00-07:00"}); } diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchAsyncClientImplTest.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchAsyncClientImplTest.java index d6540b3e7984..7782131d9bd6 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchAsyncClientImplTest.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchAsyncClientImplTest.java @@ -21,6 +21,7 @@ import static com.azure.search.documents.TestHelpers.assertHttpResponseExceptionAsync; import static com.azure.search.documents.TestHelpers.assertMapEquals; +import static com.azure.search.documents.TestHelpers.createPointGeometry; import static com.azure.search.documents.TestHelpers.uploadDocument; import static com.azure.search.documents.TestHelpers.uploadDocuments; import static com.azure.search.documents.TestHelpers.waitForIndexing; @@ -110,7 +111,7 @@ public void canGetDynamicDocument() { expectedDoc.put("Rating", 3); expectedDoc.put("Address", addressDoc); expectedDoc.put("Rooms", rooms); -// expectedDoc.put("Location", createPointGeometryString(40.760586, -73.975403)); + expectedDoc.put("Location", createPointGeometry(40.760586, -73.975403)); uploadDocument(asyncClient, expectedDoc); diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchDocumentConverterTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchDocumentConverterTests.java index 485cb6e83dd0..4b453d79126e 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchDocumentConverterTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchDocumentConverterTests.java @@ -3,12 +3,11 @@ package com.azure.search.documents; -import com.azure.core.util.serializer.JacksonAdapter; -import com.azure.core.util.serializer.SerializerEncoding; -import com.azure.search.documents.implementation.SerializationUtil; +import com.azure.core.experimental.serializer.Type; +import com.azure.core.experimental.spatial.PointGeometry; import org.junit.jupiter.api.Test; -import java.io.IOException; +import java.io.ByteArrayInputStream; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; @@ -19,7 +18,10 @@ import java.util.Map; import java.util.stream.Collectors; +import static com.azure.search.documents.TestHelpers.SERIALIZER; import static com.azure.search.documents.TestHelpers.assertMapEquals; +import static com.azure.search.documents.TestHelpers.assertObjectEquals; +import static com.azure.search.documents.TestHelpers.createPointGeometry; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -36,18 +38,10 @@ private SearchDocument deserialize(String json) { // the result object is a map of key:value, get deserialized directly into the Document object // Document is simply a Hash Map. // in this case we simulate creation of the object created by azure-core - - JacksonAdapter adapter = new JacksonAdapter(); - SerializationUtil.configureMapper(adapter.serializer()); - - SearchDocument doc = new SearchDocument(); - try { - doc = adapter.deserialize(json, SearchDocument.class, SerializerEncoding.JSON); - cleanupODataAnnotation(doc); - - } catch (IOException e) { - e.printStackTrace(); - } + Type> type = new Type>() {}; + ByteArrayInputStream inputStream = new ByteArrayInputStream(json.getBytes()); + SearchDocument doc = new SearchDocument(SERIALIZER.deserialize(inputStream, type).block()); + cleanupODataAnnotation(doc); return doc; } @@ -124,32 +118,32 @@ public void canReadArraysOfPrimitiveTypes() { } -// @Test -// public void canReadGeoPoint() { -// String json = "{ \"field\": { \"type\": \"Point\", \"coordinates\": [-122.131577, 47.678581], " -// + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\":\"EPSG:4326\"}}}}"; -// SearchDocument expectedDoc = new SearchDocument(Collections.singletonMap("field", -// createPointGeometryString(47.678581, -122.131577))); -// -// SearchDocument actualDoc = deserialize(json); -// expectedDoc.forEach((key, value) -> { -// assertObjectEquals(value, actualDoc.get(key), false, "properties"); -// }); -// } - -// @Test -// public void canReadGeoPointCollection() { -// String json = "{\"field\":[{\"type\":\"Point\", \"coordinates\":[-122.131577, 47.678581], " -// + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\":\"EPSG:4326\"}}}, " -// + "{\"type\":\"Point\", \"coordinates\":[-121.0, 49.0], " -// + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\":\"EPSG:4326\"}}}]}"; -// SearchDocument expectedDoc = new SearchDocument(Collections.singletonMap("field", -// Arrays.asList(createPointGeometryString(47.678581, -122.131577), createPointGeometryString(49.0, -// -121.0)))); -// -// SearchDocument actualDoc = deserialize(json); -// assertMapEquals(expectedDoc, actualDoc, true, "properties"); -// } + @Test + public void canReadGeoPoint() { + String json = "{ \"field\": { \"type\": \"Point\", \"coordinates\": [-122.131577, 47.678581], " + + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\":\"EPSG:4326\"}}}}"; + SearchDocument expectedDoc = new SearchDocument(Collections.singletonMap("field", + createPointGeometry(47.678581, -122.131577))); + + SearchDocument actualDoc = deserialize(json); + expectedDoc.forEach((key, value) -> { + assertObjectEquals(value, actualDoc.get(key), false, "properties"); + }); + } + + @Test + public void canReadGeoPointCollection() { + String json = "{\"field\":[{\"type\":\"Point\", \"coordinates\":[-122.131577, 47.678581], " + + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\":\"EPSG:4326\"}}}, " + + "{\"type\":\"Point\", \"coordinates\":[-121.0, 49.0], " + + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\":\"EPSG:4326\"}}}]}"; + SearchDocument expectedDoc = new SearchDocument(Collections.singletonMap("field", + Arrays.asList(createPointGeometry(47.678581, -122.131577), createPointGeometry(49.0, + -121.0)))); + + SearchDocument actualDoc = deserialize(json); + assertMapEquals(expectedDoc, actualDoc, true, "properties"); + } @Test public void canReadComplexObject() { @@ -202,26 +196,26 @@ public void canReadComplexCollection() { assertEquals(expectedDoc, actualDoc); } -// @Test -// public void canReadArraysOfMixedTypes() { -// // Azure Cognitive Search won't return payloads like this; This test is only for pinning purposes. -// String json = -// "{\"field\": [\"hello\", 123, 3.14, { \"type\": \"Point\", \"coordinates\": [-122.131577, 47.678581], " -// + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\": \"EPSG:4326\"}}}, " -// + "{ \"name\": \"Arthur\", \"quest\": null }] }"; -// -// PointGeometry point = createPointGeometryString(47.678581, -122.131577); -// SearchDocument innerDoc = new SearchDocument(); -// innerDoc.put("name", "Arthur"); -// innerDoc.put("quest", null); -// List value = Arrays.asList("hello", 123, 3.14, point, innerDoc); -// -// SearchDocument expectedDoc = new SearchDocument(); -// expectedDoc.put("field", value); -// -// SearchDocument actualDoc = deserialize(json); -// assertMapEquals(expectedDoc, actualDoc, true, "properties"); -// } + @Test + public void canReadArraysOfMixedTypes() { + // Azure Cognitive Search won't return payloads like this; This test is only for pinning purposes. + String json = + "{\"field\": [\"hello\", 123, 3.14, { \"type\": \"Point\", \"coordinates\": [-122.131577, 47.678581], " + + "\"crs\":{\"type\":\"name\", \"properties\":{\"name\": \"EPSG:4326\"}}}, " + + "{ \"name\": \"Arthur\", \"quest\": null }] }"; + + PointGeometry point = createPointGeometry(47.678581, -122.131577); + SearchDocument innerDoc = new SearchDocument(); + innerDoc.put("name", "Arthur"); + innerDoc.put("quest", null); + List value = Arrays.asList("hello", 123, 3.14, point, innerDoc); + + SearchDocument expectedDoc = new SearchDocument(); + expectedDoc.put("field", value); + + SearchDocument actualDoc = deserialize(json); + assertMapEquals(expectedDoc, actualDoc, true, "properties"); + } @Test public void dateTimeStringsAreReadAsDateTime() { diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchSyncTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchSyncTests.java index 189640b88278..f42108fac13a 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchSyncTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/SearchSyncTests.java @@ -5,8 +5,6 @@ import com.azure.core.util.Context; import com.azure.core.util.CoreUtils; -import com.azure.core.util.serializer.JacksonAdapter; -import com.azure.search.documents.implementation.SerializationUtil; import com.azure.search.documents.indexes.SearchIndexClient; import com.azure.search.documents.indexes.models.SearchField; import com.azure.search.documents.indexes.models.SearchFieldDataType; @@ -25,7 +23,6 @@ import com.azure.search.documents.test.environment.models.NonNullableModel; import com.azure.search.documents.util.SearchPagedIterable; import com.azure.search.documents.util.SearchPagedResponse; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import java.net.HttpURLConnection; @@ -47,9 +44,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.azure.search.documents.TestHelpers.SERIALIZER; import static com.azure.search.documents.TestHelpers.assertHttpResponseException; import static com.azure.search.documents.TestHelpers.assertMapEquals; import static com.azure.search.documents.TestHelpers.assertObjectEquals; +import static com.azure.search.documents.TestHelpers.createPointGeometry; import static com.azure.search.documents.TestHelpers.uploadDocuments; import static com.azure.search.documents.TestHelpers.uploadDocumentsJson; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -233,9 +232,7 @@ public void canSearchStaticallyTypedDocuments() { actualResults.add(hotel); }); } - ObjectMapper mapper = new JacksonAdapter().serializer(); - SerializationUtil.configureMapper(mapper); - List hotelsList = hotels.stream().map(hotel -> mapper.convertValue(hotel, Hotel.class)) + List hotelsList = hotels.stream().map(hotel -> SERIALIZER.convertValue(hotel, Hotel.class).block()) .collect(Collectors.toList()); assertEquals(hotelsList.size(), actualResults.size()); @@ -622,22 +619,22 @@ public void canSearchWithEscapedSpecialCharsInRegex() { assertEquals(0, resultsList.size()); } -// @Test -// public void searchWithScoringProfileBoostsScore() { -// client = setupClient(this::createHotelIndex); -// -// uploadDocumentsJson(client, HOTELS_DATA_JSON); -// SearchOptions searchOptions = new SearchOptions() -// .setScoringProfile("nearest") -// .setScoringParameters(new ScoringParameter("myloc", createPointGeometry(49.0, -122.0))) -// .setFilter("Rating eq 5 or Rating eq 1"); -// -// List> response = getSearchResults(client.search("hotel", -// searchOptions, Context.NONE)); -// assertEquals(2, response.size()); -// assertEquals(Arrays.asList("2", "1"), -// response.stream().map(res -> res.get("HotelId").toString()).collect(Collectors.toList())); -// } + @Test + public void searchWithScoringProfileBoostsScore() { + client = setupClient(this::createHotelIndex); + + uploadDocumentsJson(client, HOTELS_DATA_JSON); + SearchOptions searchOptions = new SearchOptions() + .setScoringProfile("nearest") + .setScoringParameters(new ScoringParameter("myloc", createPointGeometry(49.0, -122.0))) + .setFilter("Rating eq 5 or Rating eq 1"); + + List> response = getSearchResults(client.search("hotel", + searchOptions, Context.NONE)); + assertEquals(2, response.size()); + assertEquals(Arrays.asList("2", "1"), + response.stream().map(res -> res.get("HotelId").toString()).collect(Collectors.toList())); + } @Test public void searchWithScoringProfileEscaper() { diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/TestHelpers.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/TestHelpers.java index d31986de974c..4b3395810f4c 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/TestHelpers.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/TestHelpers.java @@ -4,13 +4,18 @@ package com.azure.search.documents; import com.azure.core.exception.HttpResponseException; +import com.azure.core.experimental.serializer.JsonInclusion; +import com.azure.core.experimental.serializer.JsonOptions; +import com.azure.core.experimental.serializer.JsonSerializer; +import com.azure.core.experimental.serializer.JsonSerializerProviders; +import com.azure.core.experimental.serializer.Type; +import com.azure.core.experimental.spatial.GeometryPosition; +import com.azure.core.experimental.spatial.PointGeometry; import com.azure.core.http.HttpPipeline; import com.azure.core.test.TestMode; import com.azure.core.util.Configuration; import com.azure.core.util.serializer.JacksonAdapter; import com.azure.core.util.serializer.SerializerEncoding; -import com.azure.search.documents.implementation.SerializationUtil; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -18,15 +23,14 @@ import reactor.test.StepVerifier; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UncheckedIOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -50,22 +54,19 @@ public final class TestHelpers { public static final String BLOB_DATASOURCE_TEST_NAME = "azs-java-test-blob"; public static final String SQL_DATASOURCE_NAME = "azs-java-test-sql"; public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - -// public static PointGeometry createPointGeometryString(Double latitude, Double longitude) { -// return new PointGeometry(new GeometryPosition(longitude, latitude), null, Collections.singletonMap("crs", new HashMap() { -// { -// put("type", "name"); -// put("properties", Collections.singletonMap("name", "EPSG:4326")); -// } -// })); -// } - - private static final ObjectMapper MAPPER; - - static { - MAPPER = new JacksonAdapter().serializer(); - SerializationUtil.configureMapper(MAPPER); + public static final JsonSerializer SERIALIZER = JsonSerializerProviders.createInstance( + new JsonOptions().setJsonInclusion(JsonInclusion.ALWAYS)); + + public static PointGeometry createPointGeometry(Double latitude, Double longitude) { + return new PointGeometry(new GeometryPosition(longitude, latitude), null, + Collections.singletonMap("crs", new HashMap() { + { + put("type", "name"); + put("properties", Collections.singletonMap("name", "EPSG:4326")); + } + })); } + /** * Assert whether two objects are equal. * @@ -288,12 +289,8 @@ public static HttpPipeline getHttpPipeline(SearchAsyncClient searchAsyncClient) } private static List> readJsonFileToList(String filename) { - Reader reader = new InputStreamReader(Objects.requireNonNull(TestHelpers.class.getClassLoader() - .getResourceAsStream(filename))); - try { - return MAPPER.readValue(reader, new TypeReference>>() { }); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + InputStream inputStream = Objects.requireNonNull(TestHelpers.class.getClassLoader() + .getResourceAsStream(filename)); + return SERIALIZER.deserialize(inputStream, new Type>>() { }).block(); } } diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/GeoPointDeserializer.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/GeoPointDeserializer.java index c29132b53746..902339e8d6d3 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/GeoPointDeserializer.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/implementation/GeoPointDeserializer.java @@ -2,8 +2,8 @@ // Licensed under the MIT License. package com.azure.search.documents.implementation; -import com.azure.search.documents.GeometryPosition; -import com.azure.search.documents.PointGeometry; +import com.azure.core.experimental.spatial.GeometryPosition; +import com.azure.core.experimental.spatial.PointGeometry; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonTokenId; import com.fasterxml.jackson.databind.DeserializationContext; @@ -51,7 +51,7 @@ public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOE * Converts an object to a GeoPoint if it is valid GeoJSON, otherwise returns the original object. * * @param obj the object to parse - * @return an instance of {@link com.azure.search.documents.PointGeometry} if valid GeoJSON, otherwise obj. + * @return an instance of {@link PointGeometry} if valid GeoJSON, otherwise obj. */ @SuppressWarnings("unchecked") private Object parseGeoPoint(Object obj) { diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/GeoPointTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/GeoPointTests.java index 6c5ffc248446..feb2953d5108 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/GeoPointTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/GeoPointTests.java @@ -3,40 +3,46 @@ package com.azure.search.documents.models; +import com.azure.core.experimental.serializer.Type; +import com.azure.core.experimental.spatial.PointGeometry; +import com.azure.core.util.Context; import com.azure.search.documents.SearchClient; import com.azure.search.documents.SearchDocument; import com.azure.search.documents.SearchTestBase; -import com.azure.search.documents.implementation.SerializationUtil; +import com.azure.search.documents.indexes.models.SearchField; +import com.azure.search.documents.indexes.models.SearchFieldDataType; +import com.azure.search.documents.indexes.models.SearchIndex; import com.azure.search.documents.util.SearchPagedIterable; import com.azure.search.documents.util.SearchPagedResponse; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import static com.azure.search.documents.TestHelpers.SERIALIZER; +import static com.azure.search.documents.TestHelpers.assertObjectEquals; +import static com.azure.search.documents.TestHelpers.createPointGeometry; import static com.azure.search.documents.TestHelpers.waitForIndexing; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class GeoPointTests extends SearchTestBase { private static final String DATA_JSON_HOTELS = "HotelsDataArray.json"; private SearchClient client; - private void uploadDocuments() throws Exception { - Reader docsData = new InputStreamReader(Objects.requireNonNull(getClass().getClassLoader() - .getResourceAsStream(GeoPointTests.DATA_JSON_HOTELS))); + private void uploadDocuments() { + InputStream docsData = Objects.requireNonNull(getClass().getClassLoader() + .getResourceAsStream(GeoPointTests.DATA_JSON_HOTELS)); - ObjectMapper mapper = new ObjectMapper(); - SerializationUtil.configureMapper(mapper); - List> documents = mapper.readValue(docsData, - new TypeReference>>() { - }); + List> documents = SERIALIZER.deserialize(docsData, + new Type>>() {}).block(); client.uploadDocuments(documents); waitForIndexing(); @@ -47,52 +53,52 @@ protected void afterTest() { getSearchIndexClientBuilder().buildClient().deleteIndex(client.getIndexName()); } -// @Test -// public void canDeserializeGeoPoint() throws Exception { -// client = getSearchClientBuilder(createHotelIndex()).buildClient(); -// -// uploadDocuments(); -// SearchOptions searchOptions = new SearchOptions().setFilter("HotelId eq '1'"); -// SearchPagedIterable results = client.search("Location", -// searchOptions, Context.NONE); -// assertNotNull(results); -// -// String expected = createPointGeometryString(47.678581, -122.131577); -// assertObjectEquals(expected, getSearchResults(results).get(0).get("Location"), -// true, "properties"); -// } -// -// @Test -// public void canSerializeGeoPoint() { -// SearchIndex index = new SearchIndex("geopoints") -// .setFields(Arrays.asList( -// new SearchField("Id", SearchFieldDataType.STRING) -// .setKey(true) -// .setFilterable(true) -// .setSortable(true), -// new SearchField("Name", SearchFieldDataType.STRING) -// .setSearchable(true) -// .setFilterable(true) -// .setSortable(true), -// new SearchField("Location", SearchFieldDataType.GEOGRAPHY_POINT) -// .setFilterable(true) -// .setSortable(true) -// )); -// -// client = getSearchClientBuilder(setupIndex(index)).buildClient(); -// -// List> docs = new ArrayList<>(); -// -// Map doc = new LinkedHashMap<>(); -// doc.put("Id", "1"); -// doc.put("Name", "test"); -// doc.put("Location", createPointGeometryString(1.0, 100.0)); -// docs.add(doc); -// IndexDocumentsResult indexResult = client.uploadDocuments(docs); -// -// assertNotNull(indexResult); -// assertTrue(indexResult.getResults().get(0).isSucceeded()); -// } + @Test + public void canDeserializeGeoPoint() { + client = getSearchClientBuilder(createHotelIndex()).buildClient(); + + uploadDocuments(); + SearchOptions searchOptions = new SearchOptions().setFilter("HotelId eq '1'"); + SearchPagedIterable results = client.search("Location", + searchOptions, Context.NONE); + assertNotNull(results); + + PointGeometry expected = createPointGeometry(47.678581, -122.131577); + assertObjectEquals(expected, getSearchResults(results).get(0).get("Location"), + true, "properties"); + } + + @Test + public void canSerializeGeoPoint() { + SearchIndex index = new SearchIndex("geopoints") + .setFields(Arrays.asList( + new SearchField("Id", SearchFieldDataType.STRING) + .setKey(true) + .setFilterable(true) + .setSortable(true), + new SearchField("Name", SearchFieldDataType.STRING) + .setSearchable(true) + .setFilterable(true) + .setSortable(true), + new SearchField("Location", SearchFieldDataType.GEOGRAPHY_POINT) + .setFilterable(true) + .setSortable(true) + )); + + client = getSearchClientBuilder(setupIndex(index)).buildClient(); + + List> docs = new ArrayList<>(); + + Map doc = new LinkedHashMap<>(); + doc.put("Id", "1"); + doc.put("Name", "test"); + doc.put("Location", createPointGeometry(1.0, 100.0)); + docs.add(doc); + IndexDocumentsResult indexResult = client.uploadDocuments(docs); + + assertNotNull(indexResult); + assertTrue(indexResult.getResults().get(0).isSucceeded()); + } private List> getSearchResults(SearchPagedIterable results) { Iterator iterator = results.iterableByPage().iterator(); diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/ScoringParameterTests.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/ScoringParameterTests.java index f5061437b4c3..ed0c1191eb2e 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/ScoringParameterTests.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/models/ScoringParameterTests.java @@ -3,12 +3,14 @@ package com.azure.search.documents.models; +import com.azure.core.experimental.spatial.PointGeometry; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static com.azure.search.documents.TestHelpers.createPointGeometry; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -55,19 +57,19 @@ public void testConstructorWithMapNullValues() { assertThrows(NullPointerException.class, () -> new ScoringParameter("null value", (List) null)); } -// @Test -// public void testConstructorWithGeopoint() { -// PointGeometry geoPoint = createPointGeometry(92.0, -114.0); -// String name = "mytest"; -// String expectValue = name + DASH + geoPoint.getPosition().getLongitude() + COMMA -// + geoPoint.getPosition().getLatitude(); -// String toFlattenString = new ScoringParameter(name, geoPoint).toString(); -// -// assertEquals(expectValue, toFlattenString); -// } -// -// @Test -// public void testConstructorWithNullGeopoint() { -// assertThrows(NullPointerException.class, () -> new ScoringParameter("null geopoint", (PointGeometry) null)); -// } + @Test + public void testConstructorWithGeopoint() { + PointGeometry geoPoint = createPointGeometry(92.0, -114.0); + String name = "mytest"; + String expectValue = name + DASH + geoPoint.getPosition().getLongitude() + COMMA + + geoPoint.getPosition().getLatitude(); + String toFlattenString = new ScoringParameter(name, geoPoint).toString(); + + assertEquals(expectValue, toFlattenString); + } + + @Test + public void testConstructorWithNullGeopoint() { + assertThrows(NullPointerException.class, () -> new ScoringParameter("null geopoint", (PointGeometry) null)); + } } diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/Hotel.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/Hotel.java index a56b50e6ad98..88a4377f729c 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/Hotel.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/Hotel.java @@ -2,6 +2,7 @@ // Licensed under the MIT License. package com.azure.search.documents.test.environment.models; +import com.azure.core.experimental.spatial.PointGeometry; import com.azure.search.documents.indexes.FieldIgnore; import com.azure.search.documents.indexes.SearchableFieldProperty; import com.azure.search.documents.indexes.SimpleFieldProperty; @@ -48,9 +49,9 @@ public class Hotel { @JsonProperty(value = "Rating") private Integer rating; -// @SimpleFieldProperty -// @JsonProperty(value = "Location") -// private PointGeometry location; + @SimpleFieldProperty + @JsonProperty(value = "Location") + private PointGeometry location; @JsonProperty(value = "Address") private HotelAddress address; @@ -154,14 +155,14 @@ public Hotel rating(Integer rating) { return this; } -// public PointGeometry location() { -// return this.location; -// } -// -// public Hotel location(PointGeometry location) { -// this.location = location; -// return this; -// } + public PointGeometry location() { + return this.location; + } + + public Hotel location(PointGeometry location) { + this.location = location; + return this; + } public HotelAddress address() { return this.address; diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/LoudHotel.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/LoudHotel.java index 453313682520..ef101b2fbd54 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/LoudHotel.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/LoudHotel.java @@ -2,6 +2,7 @@ // Licensed under the MIT License. package com.azure.search.documents.test.environment.models; +import com.azure.core.experimental.spatial.PointGeometry; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -41,9 +42,9 @@ public class LoudHotel { @JsonProperty(value = "Rating") private Integer RATING; -// @JsonProperty(value = "Location") -// @JsonInclude(JsonInclude.Include.ALWAYS) -// private PointGeometry LOCATION; + @JsonProperty(value = "Location") + @JsonInclude(JsonInclude.Include.ALWAYS) + private PointGeometry LOCATION; @JsonProperty(value = "Address") private HotelAddress ADDRESS; @@ -147,14 +148,14 @@ public LoudHotel RATING(Integer rating) { return this; } -// public PointGeometry LOCATION() { -// return this.LOCATION; -// } -// -// public LoudHotel LOCATION(PointGeometry location) { -// this.LOCATION = location; -// return this; -// } + public PointGeometry LOCATION() { + return this.LOCATION; + } + + public LoudHotel LOCATION(PointGeometry location) { + this.LOCATION = location; + return this; + } public HotelAddress ADDRESS() { return this.ADDRESS; diff --git a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/ModelWithPrimitiveCollections.java b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/ModelWithPrimitiveCollections.java index be21367876f9..d70510426747 100644 --- a/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/ModelWithPrimitiveCollections.java +++ b/sdk/search/azure-search-documents/src/test/java/com/azure/search/documents/test/environment/models/ModelWithPrimitiveCollections.java @@ -2,6 +2,7 @@ // Licensed under the MIT License. package com.azure.search.documents.test.environment.models; +import com.azure.core.experimental.spatial.PointGeometry; import com.fasterxml.jackson.annotation.JsonProperty; import java.time.OffsetDateTime; @@ -26,8 +27,8 @@ public class ModelWithPrimitiveCollections { @JsonProperty(value = "Longs") private Long[] longs; -// @JsonProperty(value = "Points") -// private PointGeometry[] points; + @JsonProperty(value = "Points") + private PointGeometry[] points; @JsonProperty(value = "Strings") private String[] strings; @@ -86,14 +87,14 @@ public Long[] longs() { return longs; } -// public ModelWithPrimitiveCollections points(PointGeometry[] points) { -// this.points = points; -// return this; -// } -// -// public PointGeometry[] points() { -// return points; -// } + public ModelWithPrimitiveCollections points(PointGeometry[] points) { + this.points = points; + return this; + } + + public PointGeometry[] points() { + return points; + } public ModelWithPrimitiveCollections strings(String[] strings) { this.strings = strings; diff --git a/sdk/search/azure-search-documents/src/test/resources/session-records/getDynamicDocumentWithEmptyObjectsReturnsObjectsFullOfNulls.json b/sdk/search/azure-search-documents/src/test/resources/session-records/getDynamicDocumentWithEmptyObjectsReturnsObjectsFullOfNulls.json index e96e7bdde7fd..181f58edbf31 100644 --- a/sdk/search/azure-search-documents/src/test/resources/session-records/getDynamicDocumentWithEmptyObjectsReturnsObjectsFullOfNulls.json +++ b/sdk/search/azure-search-documents/src/test/resources/session-records/getDynamicDocumentWithEmptyObjectsReturnsObjectsFullOfNulls.json @@ -104,4 +104,4 @@ "Exception" : null } ], "variables" : [ "hotels34241029d98ae22f9426dba8865bf60" ] -} \ No newline at end of file +} diff --git a/sdk/search/pom.xml b/sdk/search/pom.xml index fa4a3706d446..e91eda86485e 100644 --- a/sdk/search/pom.xml +++ b/sdk/search/pom.xml @@ -10,5 +10,7 @@ 1.0.0 azure-search-documents + azure-search-documents-serializer-jackson + azure-search-documents-serializer-gson