From 894bdc0d9d10d8186001050c65dd49c318561bb2 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:01:04 -0500 Subject: [PATCH 1/6] PoC for XML support in RestProxy --- .../http/rest/RestProxyUtils.java | 5 +- .../http/serializer/CompositeSerializer.java | 127 +++++++++++++++++ .../serializer/DefaultJsonSerializer.java | 37 ++++- .../http/serializer/DefaultXmlSerializer.java | 128 ++++++++++++++++++ .../core/util/binarydata/BinaryData.java | 5 +- .../core/util/serializer/JsonSerializer.java | 29 +++- .../util/serializer/ObjectSerializer.java | 83 +++++++++++- .../core/util/serializer/XmlSerializer.java | 77 +++++++++++ 8 files changed, 474 insertions(+), 17 deletions(-) create mode 100644 sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java create mode 100644 sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java create mode 100644 sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java index 781ea061548bb..c3b7828f0ce59 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java @@ -6,12 +6,14 @@ import io.clientcore.core.http.models.HttpHeaderName; import io.clientcore.core.http.models.HttpRequest; import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; +import io.clientcore.core.implementation.http.serializer.DefaultXmlSerializer; import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.binarydata.BinaryData; import io.clientcore.core.util.binarydata.InputStreamBinaryData; import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.InputStream; +import java.util.Arrays; /** * Utility methods that aid processing in RestProxy. @@ -83,6 +85,7 @@ static String bodyTooSmall(long length, long expectedLength) { * @return the default serializer */ public static ObjectSerializer createDefaultSerializer() { - return new DefaultJsonSerializer(); + return ObjectSerializer + .compositeSerializer(Arrays.asList(new DefaultJsonSerializer(), new DefaultXmlSerializer())); } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java new file mode 100644 index 0000000000000..5abec9e3f1a99 --- /dev/null +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package io.clientcore.core.implementation.http.serializer; + +import io.clientcore.core.util.ClientLogger; +import io.clientcore.core.util.serializer.ObjectSerializer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; + +/** + * An implementation of {@link ObjectSerializer} which comprises multiple serializers. + *

+ * This class is meant to aid situations where the serialization formats being used are unknown ahead of time, allowing + * the consumer of {@link ObjectSerializer} to provide a list of serializers to try in order until one is successful, + * or the list is exhausted. + */ +public final class CompositeSerializer extends ObjectSerializer { + private static final ClientLogger LOGGER = new ClientLogger(CompositeSerializer.class); + + private final List serializers; + private final EnumSet supportedFormats; + + /** + * Creates an instance of the {@link CompositeSerializer}. + * + * @param serializers The list of serializers to try in order. + */ + public CompositeSerializer(List serializers) { + Objects.requireNonNull(serializers, "The list of serializers cannot be null."); + if (serializers.isEmpty()) { + throw new IllegalArgumentException("The list of serializers cannot be empty."); + } + + this.serializers = new ArrayList<>(serializers); + this.supportedFormats = EnumSet.allOf(Format.class); + + // Loop over all serializers and remove unsupported formats. + formatters_loop: for (Format format : supportedFormats) { + for (ObjectSerializer serializer : serializers) { + if (serializer.supportsFormat(format)) { + break formatters_loop; + } + } + + // Will only be reached if none of the serializers support the format. + supportedFormats.remove(format); + } + } + + @Override + public T deserializeFromBytes(byte[] data, Type type, ObjectSerializer.Format format) throws IOException { + verifyFormat(format); + + for (ObjectSerializer serializer : serializers) { + if (serializer.supportsFormat(format)) { + return serializer.deserializeFromBytes(data, type, format); + } + } + + // Should never be reached. + throw LOGGER.logThrowableAsError( + new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); + } + + @Override + public T deserializeFromStream(InputStream stream, Type type, ObjectSerializer.Format format) + throws IOException { + verifyFormat(format); + + for (ObjectSerializer serializer : serializers) { + if (serializer.supportsFormat(format)) { + return serializer.deserializeFromStream(stream, type, format); + } + } + + // Should never be reached. + throw LOGGER.logThrowableAsError( + new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); + } + + @Override + public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) throws IOException { + verifyFormat(format); + + for (ObjectSerializer serializer : serializers) { + if (serializer.supportsFormat(format)) { + return serializer.serializeToBytes(value, format); + } + } + + // Should never be reached. + throw LOGGER.logThrowableAsError( + new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); + } + + @Override + public void serializeToStream(OutputStream stream, Object value, ObjectSerializer.Format format) + throws IOException { + verifyFormat(format); + + for (ObjectSerializer serializer : serializers) { + if (serializer.supportsFormat(format)) { + serializer.serializeToStream(stream, value, format); + return; + } + } + } + + @Override + public boolean supportsFormat(ObjectSerializer.Format format) { + return supportedFormats.contains(format); + } + + private void verifyFormat(ObjectSerializer.Format format) { + if (!supportsFormat(format)) { + throw LOGGER.logThrowableAsError( + new UnsupportedOperationException("The provided format (" + format + ") is not supported.")); + } + } +} diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java index 46e2380dcf54f..4488a52d45f53 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java @@ -10,6 +10,7 @@ import io.clientcore.core.serialization.json.JsonWriter; import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -21,13 +22,21 @@ /** * Default implementation of the {@link JsonSerializer}. */ -public class DefaultJsonSerializer implements JsonSerializer { +public class DefaultJsonSerializer extends JsonSerializer { // DefaultJsonSerializer is a commonly used class, use a static logger. private static final ClientLogger LOGGER = new ClientLogger(DefaultJsonSerializer.class); + /** + * Creates an instance of the {@link DefaultJsonSerializer}. + */ + public DefaultJsonSerializer() { + } + @SuppressWarnings("unchecked") @Override - public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { + public T deserializeFromBytes(byte[] bytes, Type type, ObjectSerializer.Format format) throws IOException { + verifyFormat(format); + try (JsonReader jsonReader = JsonProviders.createReader(bytes)) { if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { Class clazz = (Class) type; @@ -43,7 +52,10 @@ public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { @SuppressWarnings("unchecked") @Override - public T deserializeFromStream(InputStream stream, Type type) throws IOException { + public T deserializeFromStream(InputStream stream, Type type, ObjectSerializer.Format format) + throws IOException { + verifyFormat(format); + try (JsonReader jsonReader = JsonProviders.createReader(stream)) { if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { Class clazz = (Class) type; @@ -58,11 +70,17 @@ public T deserializeFromStream(InputStream stream, Type type) throws IOExcep } @Override - public byte[] serializeToBytes(Object value) throws IOException { + public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) throws IOException { + verifyFormat(format); + if (value == null) { return null; } + if (value instanceof JsonSerializable) { + return ((JsonSerializable) value).toJsonBytes(); + } + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); JsonWriter jsonWriter = JsonProviders.createWriter(byteArrayOutputStream)) { @@ -74,7 +92,10 @@ public byte[] serializeToBytes(Object value) throws IOException { } @Override - public void serializeToStream(OutputStream stream, Object value) throws IOException { + public void serializeToStream(OutputStream stream, Object value, ObjectSerializer.Format format) + throws IOException { + verifyFormat(format); + if (value == null) { return; } @@ -83,4 +104,10 @@ public void serializeToStream(OutputStream stream, Object value) throws IOExcept jsonWriter.writeUntyped(value); } } + + private static void verifyFormat(ObjectSerializer.Format format) { + if (format != ObjectSerializer.Format.JSON) { + throw LOGGER.logThrowableAsError(new UnsupportedOperationException("Only JSON format is supported.")); + } + } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java new file mode 100644 index 0000000000000..da4a10e651209 --- /dev/null +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package io.clientcore.core.implementation.http.serializer; + +import io.clientcore.core.implementation.TypeUtil; +import io.clientcore.core.serialization.xml.XmlReader; +import io.clientcore.core.serialization.xml.XmlSerializable; +import io.clientcore.core.serialization.xml.XmlWriter; +import io.clientcore.core.util.ClientLogger; +import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.XmlSerializer; + +import javax.xml.stream.XMLStreamException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; + +/** + * Default implementation of the {@link XmlSerializer}. + */ +public class DefaultXmlSerializer extends XmlSerializer { + // DefaultXmlSerializer is a commonly used class, use a static logger. + private static final ClientLogger LOGGER = new ClientLogger(DefaultXmlSerializer.class); + + /** + * Creates an instance of the {@link DefaultXmlSerializer}. + */ + public DefaultXmlSerializer() { + } + + @Override + public T deserializeFromBytes(byte[] bytes, Type type, ObjectSerializer.Format format) throws IOException { + verifyFormat(format); + + try (XmlReader xmlReader = XmlReader.fromBytes(bytes)) { + return deserializeShared(xmlReader, type); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } + + @Override + public T deserializeFromStream(InputStream stream, Type type, ObjectSerializer.Format format) + throws IOException { + verifyFormat(format); + + try (XmlReader xmlReader = XmlReader.fromStream(stream)) { + return deserializeShared(xmlReader, type); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } + + @SuppressWarnings("unchecked") + private static T deserializeShared(XmlReader xmlReader, Type type) { + try { + if (type instanceof Class && XmlSerializer.class.isAssignableFrom(TypeUtil.getRawClass(type))) { + Class clazz = (Class) type; + + return (T) clazz.getMethod("fromXml", XmlReader.class).invoke(null, xmlReader); + } else { + // TODO (alzimmer): XML needs untyped support. + throw LOGGER.logThrowableAsError(new UnsupportedOperationException( + "DefaultXmlSerializer does not have support for untyped deserialization.")); + } + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + throw LOGGER.logThrowableAsError(new RuntimeException(e)); + } + } + + @Override + public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) throws IOException { + verifyFormat(format); + + if (value == null) { + return null; + } + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + XmlWriter xmlWriter = XmlWriter.toStream(byteArrayOutputStream)) { + serializeShared(xmlWriter, value); + + return byteArrayOutputStream.toByteArray(); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } + + @Override + public void serializeToStream(OutputStream stream, Object value, ObjectSerializer.Format format) + throws IOException { + verifyFormat(format); + + if (value == null) { + return; + } + + try (XmlWriter xmlWriter = XmlWriter.toStream(stream)) { + serializeShared(xmlWriter, value); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } + + private static void serializeShared(XmlWriter xmlWriter, Object value) { + try { + if (value instanceof XmlSerializable) { + ((XmlSerializable) value).toXml(xmlWriter).flush(); + } else { + // TODO (alzimmer): XML needs untyped support. + throw LOGGER.logThrowableAsError(new UnsupportedOperationException( + "DefaultXmlSerializer does not have support for untyped serialization.")); + } + } catch (XMLStreamException e) { + throw LOGGER.logThrowableAsError(new RuntimeException(e)); + } + } + + private static void verifyFormat(ObjectSerializer.Format format) { + if (format != ObjectSerializer.Format.XML) { + throw LOGGER.logThrowableAsError(new UnsupportedOperationException("Only XML format is supported.")); + } + } +} diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java index b6942ab67cc05..318927c2d2c5e 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java @@ -4,6 +4,7 @@ package io.clientcore.core.util.binarydata; import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; +import io.clientcore.core.implementation.http.serializer.DefaultXmlSerializer; import io.clientcore.core.serialization.json.JsonWriter; import io.clientcore.core.util.serializer.ObjectSerializer; @@ -20,6 +21,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.util.Arrays; import java.util.List; /** @@ -118,7 +120,8 @@ public abstract class BinaryData implements Closeable { private static final BinaryData EMPTY = BinaryData.fromBytes(new byte[0]); - static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + static final ObjectSerializer SERIALIZER + = ObjectSerializer.compositeSerializer(Arrays.asList(new DefaultJsonSerializer(), new DefaultXmlSerializer())); static final int STREAM_READ_SIZE = 8192; static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; static final String TOO_LARGE_FOR_BYTE_ARRAY diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java index e7a38be8d7aa0..6f499cf002862 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java @@ -11,48 +11,67 @@ /** * Generic interface covering basic JSON serialization and deserialization methods. */ -public interface JsonSerializer extends ObjectSerializer { +public abstract class JsonSerializer extends ObjectSerializer { + /** + * Creates an instance of the {@link JsonSerializer}. + */ + public JsonSerializer() { + } + /** * Reads a JSON byte array into its object representation. * * @param data The JSON byte array. * @param type {@link Type} representing the object. + * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized JSON byte array. + * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the deserialization fails. */ @Override - T deserializeFromBytes(byte[] data, Type type) throws IOException; + public abstract T deserializeFromBytes(byte[] data, Type type, Format format) throws IOException; /** * Reads a JSON stream into its object representation. * * @param stream JSON stream. * @param type {@link Type} representing the object. + * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized JSON stream. + * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the deserialization fails. */ @Override - T deserializeFromStream(InputStream stream, Type type) throws IOException; + public abstract T deserializeFromStream(InputStream stream, Type type, Format format) throws IOException; /** * Converts the object into a JSON byte array. * * @param value The object. + * @param format The format to deserialize the object from. * @return The JSON binary representation of the serialized object. + * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the serialization fails. */ @Override - byte[] serializeToBytes(Object value) throws IOException; + public abstract byte[] serializeToBytes(Object value, Format format) throws IOException; /** * Writes an object's JSON representation into a stream. * * @param stream {@link OutputStream} where the object's JSON representation will be written. * @param value The object to serialize. + * @param format The format to deserialize the object from. + * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the serialization fails. */ @Override - void serializeToStream(OutputStream stream, Object value) throws IOException; + public abstract void serializeToStream(OutputStream stream, Object value, Format format) throws IOException; + + @Override + public final boolean supportsFormat(Format format) { + return format == Format.JSON; + } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java index 07088db3521b2..886fad22cbdf6 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java @@ -3,52 +3,125 @@ package io.clientcore.core.util.serializer; +import io.clientcore.core.implementation.http.serializer.CompositeSerializer; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Type; +import java.util.List; /** * Generic interface covering serializing and deserialization objects. */ -public interface ObjectSerializer { +public abstract class ObjectSerializer { + /** + * Creates an instance of {@link ObjectSerializer}. + */ + public ObjectSerializer() { + } + + /** + * Creates an instance of {@link ObjectSerializer} which comprises multiple serializers. + * + * @param serializers The serializers. + * @return An instance of {@link ObjectSerializer}. + * @throws NullPointerException If {@code serializers} is null. + * @throws IllegalArgumentException If {@code serializers} is empty. + */ + public static ObjectSerializer compositeSerializer(List serializers) { + return new CompositeSerializer(serializers); + } + /** * Reads a byte array into its object representation. + *

+ * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an + * {@link UnsupportedOperationException} will be thrown. * * @param data The byte array. * @param type {@link Type} representing the object. + * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized byte array. + * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the deserialization fails. */ - T deserializeFromBytes(byte[] data, Type type) throws IOException; + public abstract T deserializeFromBytes(byte[] data, Type type, Format format) throws IOException; /** * Reads a stream into its object representation. + *

+ * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an + * {@link UnsupportedOperationException} will be thrown. * * @param stream {@link InputStream} of data. * @param type {@link Type} representing the object. + * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized stream. + * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the deserialization fails. */ - T deserializeFromStream(InputStream stream, Type type) throws IOException; + public abstract T deserializeFromStream(InputStream stream, Type type, Format format) throws IOException; /** * Serializes an object into a byte array. + *

+ * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an + * {@link UnsupportedOperationException} will be thrown. * * @param value The object to serialize. + * @param format The format to serialize the object in. * @return The binary representation of the serialized object. + * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the serialization fails. */ - byte[] serializeToBytes(Object value) throws IOException; + public abstract byte[] serializeToBytes(Object value, Format format) throws IOException; /** * Serializes and writes an object into a provided stream. + *

+ * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an + * {@link UnsupportedOperationException} will be thrown. * * @param stream {@link OutputStream} where the serialized object will be written. * @param value The object to serialize. + * @param format The format to serialize the object in. + * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the serialization fails. */ - void serializeToStream(OutputStream stream, Object value) throws IOException; + public abstract void serializeToStream(OutputStream stream, Object value, Format format) throws IOException; + + /** + * Indicates whether the given implementation of {@link ObjectSerializer} supports the provided format. + *

+ * An implementation of {@link ObjectSerializer} may support multiple formats, such as JSON and XML. + *

+ * A check for support should be made before attempting to serialize or deserialize an object. + * + * @param format The format to check support for. + * @return Whether the format is supported. + */ + public abstract boolean supportsFormat(Format format); + + /** + * Formats supported by {@link ObjectSerializer}, and its subclasses. + */ + public enum Format { + /** + * JSON format. + */ + JSON, + + /** + * Text format. + */ + TEXT, + + /** + * XML format. + */ + XML + } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java new file mode 100644 index 0000000000000..e0f669ecbab8e --- /dev/null +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package io.clientcore.core.util.serializer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Type; + +/** + * Generic interface covering basic XML serialization and deserialization methods. + */ +public abstract class XmlSerializer extends ObjectSerializer { + /** + * Creates an instance of the {@link XmlSerializer}. + */ + public XmlSerializer() { + } + + /** + * Reads an XML byte array into its object representation. + * + * @param data The XML byte array. + * @param type {@link Type} representing the object. + * @param format The format to deserialize the object from. + * @param Type of the object. + * @return The object represented by the deserialized XML byte array. + * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. + * @throws IOException If the deserialization fails. + */ + @Override + public abstract T deserializeFromBytes(byte[] data, Type type, Format format) throws IOException; + + /** + * Reads an XML stream into its object representation. + * + * @param stream XML stream. + * @param type {@link Type} representing the object. + * @param format The format to deserialize the object from. + * @param Type of the object. + * @return The object represented by the deserialized XML stream. + * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. + * @throws IOException If the deserialization fails. + */ + @Override + public abstract T deserializeFromStream(InputStream stream, Type type, Format format) throws IOException; + + /** + * Converts the object into an XML byte array. + * + * @param value The object. + * @param format The format to deserialize the object from. + * @return The XML binary representation of the serialized object. + * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. + * @throws IOException If the serialization fails. + */ + @Override + public abstract byte[] serializeToBytes(Object value, Format format) throws IOException; + + /** + * Writes an object's XML representation into a stream. + * + * @param stream {@link OutputStream} where the object's XML representation will be written. + * @param value The object to serialize. + * @param format The format to deserialize the object from. + * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. + * @throws IOException If the serialization fails. + */ + @Override + public abstract void serializeToStream(OutputStream stream, Object value, Format format) throws IOException; + + @Override + public final boolean supportsFormat(Format format) { + return format == Format.XML; + } +} From b0d824c1016520bc1ebd376d5ce5d7b66bfbf634 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:34:39 -0500 Subject: [PATCH 2/6] Hide the need for format in serialization calls --- .../io/clientcore/core/http/RestProxy.java | 27 +++-- .../http/rest/RequestDataConfiguration.java | 8 +- .../http/rest/RestProxyBase.java | 28 ++--- .../http/rest/RestProxyImpl.java | 110 ++++++++++++++++-- .../http/rest/RestProxyUtils.java | 14 --- .../http/rest/SwaggerMethodParser.java | 39 ++++--- .../http/serializer/CompositeSerializer.java | 76 ++++-------- .../serializer/DefaultJsonSerializer.java | 25 +--- .../http/serializer/DefaultXmlSerializer.java | 25 +--- .../serializer/HttpResponseBodyDecoder.java | 25 ++-- .../core/util/binarydata/BinaryData.java | 5 +- .../core/util/serializer/JsonSerializer.java | 20 +--- .../util/serializer/ObjectSerializer.java | 65 +---------- .../util/serializer/SerializationFormat.java | 23 ++++ .../core/util/serializer/XmlSerializer.java | 20 +--- .../http/rest/SwaggerMethodParserTests.java | 5 +- .../HttpResponseBodyDecoderTests.java | 9 +- .../core/shared/HttpClientTests.java | 8 +- .../core/util/serializer/MockSerializer.java | 2 +- 19 files changed, 258 insertions(+), 276 deletions(-) create mode 100644 sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/SerializationFormat.java diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java index 8222b45909645..056abef4e8f37 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java @@ -9,6 +9,8 @@ import io.clientcore.core.implementation.http.rest.RestProxyUtils; import io.clientcore.core.implementation.http.rest.SwaggerInterfaceParser; import io.clientcore.core.implementation.http.rest.SwaggerMethodParser; +import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; +import io.clientcore.core.implementation.http.serializer.DefaultXmlSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import java.lang.reflect.InvocationHandler; @@ -29,13 +31,14 @@ public final class RestProxy implements InvocationHandler { * Create a RestProxy. * * @param httpPipeline the HttpPipelinePolicy and HttpClient httpPipeline that will be used to send HTTP requests. - * @param serializer the serializer that will be used to convert response bodies to POJOs. * @param interfaceParser the parser that contains information about the interface describing REST API methods that * this RestProxy "implements". + * @param serializers the serializers that will be used to convert response bodies to POJOs. */ - private RestProxy(HttpPipeline httpPipeline, ObjectSerializer serializer, SwaggerInterfaceParser interfaceParser) { + private RestProxy(HttpPipeline httpPipeline, SwaggerInterfaceParser interfaceParser, + ObjectSerializer... serializers) { this.interfaceParser = interfaceParser; - this.restProxyImpl = new RestProxyImpl(httpPipeline, serializer, interfaceParser); + this.restProxyImpl = new RestProxyImpl(httpPipeline, interfaceParser, serializers); } /** @@ -68,24 +71,30 @@ public Object invoke(Object proxy, final Method method, Object[] args) { * @param the type of the Swagger interface * @return a proxy implementation of the provided Swagger interface */ + @SuppressWarnings("unchecked") public static A create(Class swaggerInterface, HttpPipeline httpPipeline) { - return create(swaggerInterface, httpPipeline, RestProxyUtils.createDefaultSerializer()); + final SwaggerInterfaceParser interfaceParser = SwaggerInterfaceParser.getInstance(swaggerInterface); + final RestProxy restProxy + = new RestProxy(httpPipeline, interfaceParser, new DefaultJsonSerializer(), new DefaultXmlSerializer()); + + return (A) Proxy.newProxyInstance(swaggerInterface.getClassLoader(), new Class[] { swaggerInterface }, + restProxy); } /** * Create a proxy implementation of the provided Swagger interface. * * @param swaggerInterface the Swagger interface to provide a proxy implementation for - * @param httpPipeline the HttpPipelinePolicy and HttpClient pipline that will be used to send Http requests - * @param serializer the serializer that will be used to convert POJOs to and from request and response bodies + * @param httpPipeline the HttpPipelinePolicy and HttpClient pipeline that will be used to send Http requests + * @param serializers the serializers that will be used to convert POJOs to and from request and response bodies * @param the type of the Swagger interface. - * * @return a proxy implementation of the provided Swagger interface + * @throws IllegalArgumentException If {@code serializers} is null or empty. */ @SuppressWarnings("unchecked") - public static A create(Class swaggerInterface, HttpPipeline httpPipeline, ObjectSerializer serializer) { + public static A create(Class swaggerInterface, HttpPipeline httpPipeline, ObjectSerializer... serializers) { final SwaggerInterfaceParser interfaceParser = SwaggerInterfaceParser.getInstance(swaggerInterface); - final RestProxy restProxy = new RestProxy(httpPipeline, serializer, interfaceParser); + final RestProxy restProxy = new RestProxy(httpPipeline, interfaceParser, serializers); return (A) Proxy.newProxyInstance(swaggerInterface.getClassLoader(), new Class[] { swaggerInterface }, restProxy); diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RequestDataConfiguration.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RequestDataConfiguration.java index 895d4a7faf7d3..7f004869c3a09 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RequestDataConfiguration.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RequestDataConfiguration.java @@ -6,10 +6,10 @@ import io.clientcore.core.http.models.HttpRequest; public class RequestDataConfiguration { - private HttpRequest httpRequest; - private SwaggerMethodParser methodParser; - private boolean isJson; - private Object bodyContent; + private final HttpRequest httpRequest; + private final SwaggerMethodParser methodParser; + private final boolean isJson; + private final Object bodyContent; public RequestDataConfiguration(HttpRequest httpRequest, SwaggerMethodParser swaggerMethodParser, boolean isJson, Object requestBodyContent) { diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java index 968d0629a306f..93808198ff941 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java @@ -16,6 +16,7 @@ import io.clientcore.core.implementation.ReflectiveInvoker; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.implementation.http.UnexpectedExceptionInformation; +import io.clientcore.core.implementation.http.serializer.CompositeSerializer; import io.clientcore.core.implementation.http.serializer.MalformedValueException; import io.clientcore.core.implementation.util.UriBuilder; import io.clientcore.core.serialization.json.JsonSerializable; @@ -30,32 +31,31 @@ import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.Arrays; public abstract class RestProxyBase { static final ResponseConstructorsCache RESPONSE_CONSTRUCTORS_CACHE = new ResponseConstructorsCache(); - private static final ResponseExceptionConstructorCache RESPONSE_EXCEPTION_CONSTRUCTOR_CACHE - = new ResponseExceptionConstructorCache(); // RestProxy is a commonly used class, use a static logger. static final ClientLogger LOGGER = new ClientLogger(RestProxyBase.class); final HttpPipeline httpPipeline; - final ObjectSerializer serializer; + final CompositeSerializer serializer; final SwaggerInterfaceParser interfaceParser; /** * Create a RestProxy. * * @param httpPipeline The HttpPipelinePolicy and HttpClient httpPipeline that will be used to send HTTP requests. - * @param serializer The serializer that will be used to convert response bodies to POJOs. * @param interfaceParser The parser that contains information about the interface describing REST API methods that * this RestProxy "implements". + * @param serializers The serializers that will be used to convert response bodies to POJOs. */ - public RestProxyBase(HttpPipeline httpPipeline, ObjectSerializer serializer, - SwaggerInterfaceParser interfaceParser) { + public RestProxyBase(HttpPipeline httpPipeline, SwaggerInterfaceParser interfaceParser, + ObjectSerializer... serializers) { this.httpPipeline = httpPipeline; - this.serializer = serializer; this.interfaceParser = interfaceParser; + this.serializer = new CompositeSerializer(Arrays.asList(serializers)); } public final Object invoke(Object proxy, RequestOptions options, SwaggerMethodParser methodParser, Object[] args) { @@ -74,7 +74,7 @@ public final Object invoke(Object proxy, RequestOptions options, SwaggerMethodPa protected abstract Object invoke(Object proxy, SwaggerMethodParser methodParser, HttpRequest request); public abstract void updateRequest(RequestDataConfiguration requestDataConfiguration, - ObjectSerializer objectSerializer) throws IOException; + CompositeSerializer serializer) throws IOException; @SuppressWarnings({ "unchecked" }) public Response createResponseIfNecessary(Response response, Type entityType, Object bodyAsObject) { @@ -103,12 +103,10 @@ public Response createResponseIfNecessary(Response response, Type entityTy * * @param methodParser The Swagger method parser to use. * @param args The arguments to use to populate the method's annotation values. - * * @return An HttpRequest. - * * @throws IOException If the body contents cannot be serialized. */ - HttpRequest createHttpRequest(SwaggerMethodParser methodParser, ObjectSerializer objectSerializer, Object[] args) + HttpRequest createHttpRequest(SwaggerMethodParser methodParser, CompositeSerializer serializer, Object[] args) throws IOException, URISyntaxException { // Sometimes people pass in a full URI for the value of their PathParam annotated argument. @@ -145,7 +143,7 @@ HttpRequest createHttpRequest(SwaggerMethodParser methodParser, ObjectSerializer final URI uri = uriBuilder.toUri(); final HttpRequest request - = configRequest(new HttpRequest(methodParser.getHttpMethod(), uri), methodParser, objectSerializer, args); + = configRequest(new HttpRequest(methodParser.getHttpMethod(), uri), methodParser, serializer, args); // Headers from Swagger method arguments always take precedence over inferred headers from body types HttpHeaders httpHeaders = request.getHeaders(); @@ -155,7 +153,7 @@ HttpRequest createHttpRequest(SwaggerMethodParser methodParser, ObjectSerializer } private HttpRequest configRequest(HttpRequest request, SwaggerMethodParser methodParser, - ObjectSerializer objectSerializer, Object[] args) throws IOException { + CompositeSerializer objectSerializer, Object[] args) throws IOException { final Object bodyContentObject = methodParser.setBody(args, serializer); if (bodyContentObject == null) { @@ -218,7 +216,6 @@ private HttpRequest configRequest(HttpRequest request, SwaggerMethodParser metho * @param response The http response to parse when constructing exception * @param responseBody The response body to use when constructing exception * @param responseDecodedBody The decoded response content to use when constructing exception - * * @return The {@link HttpResponseException} created from the provided details. */ public static HttpResponseException instantiateUnexpectedException( @@ -261,7 +258,6 @@ public static HttpResponseException instantiateUnexpectedException( * Whether {@code JsonSerializable} is supported and the {@code bodyContentClass} is an instance of it. * * @param bodyContentClass The body content class. - * * @return Whether {@code bodyContentClass} can be used as {@code JsonSerializable}. */ static boolean supportsJsonSerializable(Class bodyContentClass) { @@ -272,9 +268,7 @@ static boolean supportsJsonSerializable(Class bodyContentClass) { * Serializes the {@code jsonSerializable} as an instance of {@code JsonSerializable}. * * @param jsonSerializable The {@code JsonSerializable} body content. - * * @return The {@link ByteBuffer} representing the serialized {@code jsonSerializable}. - * * @throws IOException If an error occurs during serialization. */ static ByteBuffer serializeAsJsonSerializable(JsonSerializable jsonSerializable) throws IOException { diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java index 28f93a500df85..dbf0077c780a1 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java @@ -3,6 +3,8 @@ package io.clientcore.core.implementation.http.rest; +import io.clientcore.core.http.models.HttpHeaderName; +import io.clientcore.core.http.models.HttpHeaders; import io.clientcore.core.http.models.HttpMethod; import io.clientcore.core.http.models.HttpRequest; import io.clientcore.core.http.models.HttpResponse; @@ -12,9 +14,12 @@ import io.clientcore.core.http.pipeline.HttpPipeline; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.implementation.http.HttpResponseAccessHelper; +import io.clientcore.core.implementation.http.serializer.CompositeSerializer; import io.clientcore.core.implementation.util.Base64Uri; +import io.clientcore.core.implementation.util.ImplUtils; import io.clientcore.core.util.binarydata.BinaryData; import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.SerializationFormat; import java.io.IOException; import java.io.InputStream; @@ -31,13 +36,13 @@ public class RestProxyImpl extends RestProxyBase { * Create a RestProxy. * * @param httpPipeline The HttpPipelinePolicy and HttpClient httpPipeline that will be used to send HTTP requests. - * @param serializer The serializer that will be used to convert response bodies to POJOs. * @param interfaceParser The parser that contains information about the interface describing REST API methods * to be used. + * @param serializers The serializers that will be used to convert response bodies to POJOs. */ - public RestProxyImpl(HttpPipeline httpPipeline, ObjectSerializer serializer, - SwaggerInterfaceParser interfaceParser) { - super(httpPipeline, serializer, interfaceParser); + public RestProxyImpl(HttpPipeline httpPipeline, SwaggerInterfaceParser interfaceParser, + ObjectSerializer... serializers) { + super(httpPipeline, interfaceParser, serializers); } /** @@ -211,7 +216,7 @@ private Object handleRestReturnType(Response response, SwaggerMethodParser me return result; } - public void updateRequest(RequestDataConfiguration requestDataConfiguration, ObjectSerializer serializerAdapter) { + public void updateRequest(RequestDataConfiguration requestDataConfiguration, CompositeSerializer serializer) { boolean isJson = requestDataConfiguration.isJson(); HttpRequest request = requestDataConfiguration.getHttpRequest(); @@ -228,7 +233,8 @@ public void updateRequest(RequestDataConfiguration requestDataConfiguration, Obj } if (isJson) { - request.setBody(BinaryData.fromObject(bodyContentObject, serializerAdapter)); + request.setBody( + BinaryData.fromObject(bodyContentObject, serializer.getSerializerForFormat(SerializationFormat.JSON))); } else if (bodyContentObject instanceof byte[]) { request.setBody(BinaryData.fromBytes((byte[]) bodyContentObject)); } else if (bodyContentObject instanceof String) { @@ -243,7 +249,97 @@ public void updateRequest(RequestDataConfiguration requestDataConfiguration, Obj request.setBody(BinaryData.fromBytes(array)); } } else { - request.setBody(BinaryData.fromObject(bodyContentObject, serializerAdapter)); + request.setBody(BinaryData.fromObject(bodyContentObject, + serializer.getSerializerForFormat(serializationFormatFromContentType(request.getHeaders())))); } } + + /** + * Determines the serializer encoding to use based on the Content-Type header. + * + * @param headers the headers to get the Content-Type to check the encoding for. + * @return the serializer encoding to use for the body. {@link SerializationFormat#JSON} if there is no Content-Type + * header or an unrecognized Content-Type encoding is given. + */ + public static SerializationFormat serializationFormatFromContentType(HttpHeaders headers) { + if (headers == null) { + return SerializationFormat.JSON; + } + + String contentType = headers.getValue(HttpHeaderName.CONTENT_TYPE); + if (ImplUtils.isNullOrEmpty(contentType)) { + // When in doubt, JSON! + return SerializationFormat.JSON; + } + + int contentTypeEnd = contentType.indexOf(';'); + contentType = (contentTypeEnd == -1) ? contentType : contentType.substring(0, contentTypeEnd); + SerializationFormat encoding = checkForKnownEncoding(contentType); + if (encoding != null) { + return encoding; + } + + int contentTypeTypeSplit = contentType.indexOf('/'); + if (contentTypeTypeSplit == -1) { + return SerializationFormat.JSON; + } + + // Check the suffix if it does not match the full types. + // Suffixes are defined by the Structured Syntax Suffix Registry + // https://www.rfc-editor.org/rfc/rfc6839 + final String subtype = contentType.substring(contentTypeTypeSplit + 1); + final int lastIndex = subtype.lastIndexOf('+'); + if (lastIndex == -1) { + return SerializationFormat.JSON; + } + + // Only XML and JSON are supported suffixes, there is no suffix for TEXT. + final String mimeTypeSuffix = subtype.substring(lastIndex + 1); + if ("xml".equalsIgnoreCase(mimeTypeSuffix)) { + return SerializationFormat.XML; + } else if ("json".equalsIgnoreCase(mimeTypeSuffix)) { + return SerializationFormat.JSON; + } + + return SerializationFormat.JSON; + } + + /* + * There is a limited set of serialization encodings that are known ahead of time. Instead of using a TreeMap with + * a case-insensitive comparator, use an optimized search specifically for the known encodings. + */ + private static SerializationFormat checkForKnownEncoding(String contentType) { + int length = contentType.length(); + + // Check the length of the content type first as it is a quick check. + if (length != 8 && length != 9 && length != 10 && length != 15 && length != 16) { + return null; + } + + if ("text/".regionMatches(true, 0, contentType, 0, 5)) { + if (length == 8) { + if ("xml".regionMatches(true, 0, contentType, 5, 3)) { + return SerializationFormat.XML; + } else if ("csv".regionMatches(true, 0, contentType, 5, 3)) { + return SerializationFormat.TEXT; + } else if ("css".regionMatches(true, 0, contentType, 5, 3)) { + return SerializationFormat.TEXT; + } + } else if (length == 9 && "html".regionMatches(true, 0, contentType, 5, 4)) { + return SerializationFormat.TEXT; + } else if (length == 10 && "plain".regionMatches(true, 0, contentType, 5, 5)) { + return SerializationFormat.TEXT; + } else if (length == 15 && "javascript".regionMatches(true, 0, contentType, 5, 10)) { + return SerializationFormat.TEXT; + } + } else if ("application/".regionMatches(true, 0, contentType, 0, 12)) { + if (length == 16 && "json".regionMatches(true, 0, contentType, 12, 4)) { + return SerializationFormat.JSON; + } else if (length == 15 && "xml".regionMatches(true, 0, contentType, 12, 3)) { + return SerializationFormat.XML; + } + } + + return null; + } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java index c3b7828f0ce59..5fd08552d3ed9 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyUtils.java @@ -5,15 +5,11 @@ import io.clientcore.core.http.models.HttpHeaderName; import io.clientcore.core.http.models.HttpRequest; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; -import io.clientcore.core.implementation.http.serializer.DefaultXmlSerializer; import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.binarydata.BinaryData; import io.clientcore.core.util.binarydata.InputStreamBinaryData; -import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.InputStream; -import java.util.Arrays; /** * Utility methods that aid processing in RestProxy. @@ -78,14 +74,4 @@ static String bodyTooLarge(long length, long expectedLength) { static String bodyTooSmall(long length, long expectedLength) { return "Request body emitted " + length + " bytes, less than the expected " + expectedLength + " bytes."; } - - /** - * Create an instance of the default serializer. - * - * @return the default serializer - */ - public static ObjectSerializer createDefaultSerializer() { - return ObjectSerializer - .compositeSerializer(Arrays.asList(new DefaultJsonSerializer(), new DefaultXmlSerializer())); - } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParser.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParser.java index 7444d2989ec38..b889ccafa33f8 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParser.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParser.java @@ -23,6 +23,7 @@ import io.clientcore.core.implementation.AccessibleByteArrayOutputStream; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.implementation.http.UnexpectedExceptionInformation; +import io.clientcore.core.implementation.http.serializer.CompositeSerializer; import io.clientcore.core.implementation.http.serializer.HttpResponseDecodeData; import io.clientcore.core.implementation.util.Base64Uri; import io.clientcore.core.implementation.util.DateTimeRfc1123; @@ -30,7 +31,7 @@ import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.ExpandableEnum; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.SerializationFormat; import java.io.IOException; import java.io.InputStream; @@ -324,14 +325,15 @@ public HttpMethod getHttpMethod() { * * @param swaggerMethodArguments The arguments to use for scheme and host substitutions. * @param uriBuilder The {@link UriBuilder} that will have its scheme and host set. - * @param serializer {@link ObjectSerializer} that is used to encode host substitutions. + * @param serializer {@link CompositeSerializer} that is used to encode host substitutions. */ - public void setSchemeAndHost(Object[] swaggerMethodArguments, UriBuilder uriBuilder, ObjectSerializer serializer) { + public void setSchemeAndHost(Object[] swaggerMethodArguments, UriBuilder uriBuilder, + CompositeSerializer serializer) { setSchemeAndHost(rawHost, hostSubstitutions, swaggerMethodArguments, uriBuilder, serializer); } static void setSchemeAndHost(String rawHost, List hostSubstitutions, - Object[] swaggerMethodArguments, UriBuilder uriBuilder, ObjectSerializer serializer) { + Object[] swaggerMethodArguments, UriBuilder uriBuilder, CompositeSerializer serializer) { final String substitutedHost = applySubstitutions(rawHost, hostSubstitutions, swaggerMethodArguments, serializer); int index = substitutedHost.indexOf("://"); @@ -355,11 +357,11 @@ static void setSchemeAndHost(String rawHost, List host * Get the path that will be used to complete the Swagger method's request. * * @param methodArguments The method arguments to use with the path substitutions. - * @param serializer {@link ObjectSerializer} that is used to encode path substitutions. + * @param serializer {@link CompositeSerializer} that is used to encode path substitutions. * * @return The path value with its placeholders replaced by the matching substitutions. */ - public String setPath(Object[] methodArguments, ObjectSerializer serializer) { + public String setPath(Object[] methodArguments, CompositeSerializer serializer) { return applySubstitutions(relativePath, pathSubstitutions, methodArguments, serializer); } @@ -369,11 +371,11 @@ public String setPath(Object[] methodArguments, ObjectSerializer serializer) { * * @param swaggerMethodArguments The arguments that will be used to create the query parameters' values. * @param uriBuilder The {@link UriBuilder} where the encoded query parameters will be set. - * @param serializer {@link ObjectSerializer} that is used to encode the query parameters. + * @param serializer {@link CompositeSerializer} that is used to encode the query parameters. */ @SuppressWarnings("unchecked") public void setEncodedQueryParameters(Object[] swaggerMethodArguments, UriBuilder uriBuilder, - ObjectSerializer serializer) { + CompositeSerializer serializer) { // First we add the constant query parameters. for (Map.Entry> entry : queryParams.entrySet()) { if (entry.getValue() == null || entry.getValue().isEmpty()) { @@ -418,9 +420,9 @@ public void setEncodedQueryParameters(Object[] swaggerMethodArguments, UriBuilde * * @param swaggerMethodArguments The arguments that will be used to create the headers' values. * @param headers The {@link HttpHeaders} where the header values will be set. - * @param serializer {@link ObjectSerializer} that is used to serialize the header values. + * @param serializer {@link CompositeSerializer} that is used to serialize the header values. */ - public void setHeaders(Object[] swaggerMethodArguments, HttpHeaders headers, ObjectSerializer serializer) { + public void setHeaders(Object[] swaggerMethodArguments, HttpHeaders headers, CompositeSerializer serializer) { headers.setAll(requestHeaders); if (swaggerMethodArguments == null) { @@ -524,12 +526,12 @@ public UnexpectedExceptionInformation getUnexpectedException(int code) { * Get the object to be used as the value of the HTTP request. * * @param swaggerMethodArguments The method arguments to get the value object from. - * @param serializer The {@link ObjectSerializer} used to encode the request body if it's an + * @param serializer The {@link CompositeSerializer} used to encode the request body if it's an * {@code application/x-www-form-urlencoded} request. * * @return The object that will be used as the body of the HTTP request. */ - public Object setBody(Object[] swaggerMethodArguments, ObjectSerializer serializer) { + public Object setBody(Object[] swaggerMethodArguments, CompositeSerializer serializer) { Object result = null; if (bodyContentMethodParameterIndex != null @@ -590,7 +592,7 @@ public Type getReturnValueWireType() { return returnValueWireType; } - private static void addSerializedQueryParameter(ObjectSerializer adapter, Object value, boolean shouldEncode, + private static void addSerializedQueryParameter(CompositeSerializer adapter, Object value, boolean shouldEncode, UriBuilder uriBuilder, String parameterName) { String parameterValue = serialize(adapter, value); @@ -604,7 +606,7 @@ private static void addSerializedQueryParameter(ObjectSerializer adapter, Object } } - private static String serialize(ObjectSerializer serializer, Object value) { + private static String serialize(CompositeSerializer serializer, Object value) { if (value == null) { return null; } @@ -627,7 +629,7 @@ private static String serialize(ObjectSerializer serializer, Object value) { return ((OffsetDateTime) value).format(DateTimeFormatter.ISO_INSTANT); } else { try (AccessibleByteArrayOutputStream outputStream = new AccessibleByteArrayOutputStream()) { - serializer.serializeToStream(outputStream, value); + serializer.serializeToStream(outputStream, value, SerializationFormat.JSON); return outputStream.toString(StandardCharsets.UTF_8); } catch (IOException e) { @@ -636,7 +638,7 @@ private static String serialize(ObjectSerializer serializer, Object value) { } } - private static String serializeFormData(ObjectSerializer serializer, String key, Object value, + private static String serializeFormData(CompositeSerializer serializer, String key, Object value, boolean shouldEncode) { if (value == null) { return null; @@ -655,7 +657,8 @@ private static String serializeFormData(ObjectSerializer serializer, String key, } } - private static String serializeAndEncodeFormValue(ObjectSerializer serializer, Object value, boolean shouldEncode) { + private static String serializeAndEncodeFormValue(CompositeSerializer serializer, Object value, + boolean shouldEncode) { if (value == null) { return null; } @@ -666,7 +669,7 @@ private static String serializeAndEncodeFormValue(ObjectSerializer serializer, O } private static String applySubstitutions(String originalValue, List substitutions, - Object[] methodArguments, ObjectSerializer serializer) { + Object[] methodArguments, CompositeSerializer serializer) { if (methodArguments == null || isNullOrEmpty(substitutions)) { return originalValue; } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java index 5abec9e3f1a99..b56a5395858ec 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java @@ -4,28 +4,24 @@ import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.SerializationFormat; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.EnumSet; import java.util.List; import java.util.Objects; /** - * An implementation of {@link ObjectSerializer} which comprises multiple serializers. - *

- * This class is meant to aid situations where the serialization formats being used are unknown ahead of time, allowing - * the consumer of {@link ObjectSerializer} to provide a list of serializers to try in order until one is successful, - * or the list is exhausted. + * An internal type that comprises multiple {@link ObjectSerializer}s and adds functionality to determine which + * {@link SerializationFormat} to use each serialization and deserialize operation. */ -public final class CompositeSerializer extends ObjectSerializer { +public final class CompositeSerializer { private static final ClientLogger LOGGER = new ClientLogger(CompositeSerializer.class); private final List serializers; - private final EnumSet supportedFormats; /** * Creates an instance of the {@link CompositeSerializer}. @@ -39,89 +35,61 @@ public CompositeSerializer(List serializers) { } this.serializers = new ArrayList<>(serializers); - this.supportedFormats = EnumSet.allOf(Format.class); - - // Loop over all serializers and remove unsupported formats. - formatters_loop: for (Format format : supportedFormats) { - for (ObjectSerializer serializer : serializers) { - if (serializer.supportsFormat(format)) { - break formatters_loop; - } - } - - // Will only be reached if none of the serializers support the format. - supportedFormats.remove(format); - } } - @Override - public T deserializeFromBytes(byte[] data, Type type, ObjectSerializer.Format format) throws IOException { - verifyFormat(format); - + public T deserializeFromBytes(byte[] data, Type type, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { - return serializer.deserializeFromBytes(data, type, format); + return serializer.deserializeFromBytes(data, type); } } - // Should never be reached. throw LOGGER.logThrowableAsError( new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } - @Override - public T deserializeFromStream(InputStream stream, Type type, ObjectSerializer.Format format) - throws IOException { - verifyFormat(format); - + public T deserializeFromStream(InputStream stream, Type type, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { - return serializer.deserializeFromStream(stream, type, format); + return serializer.deserializeFromStream(stream, type); } } - // Should never be reached. throw LOGGER.logThrowableAsError( new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } - @Override - public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) throws IOException { - verifyFormat(format); - + public byte[] serializeToBytes(Object value, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { - return serializer.serializeToBytes(value, format); + return serializer.serializeToBytes(value); } } - // Should never be reached. throw LOGGER.logThrowableAsError( new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } - @Override - public void serializeToStream(OutputStream stream, Object value, ObjectSerializer.Format format) - throws IOException { - verifyFormat(format); - + public void serializeToStream(OutputStream stream, Object value, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { - serializer.serializeToStream(stream, value, format); + serializer.serializeToStream(stream, value); return; } } - } - @Override - public boolean supportsFormat(ObjectSerializer.Format format) { - return supportedFormats.contains(format); + throw LOGGER.logThrowableAsError( + new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } - private void verifyFormat(ObjectSerializer.Format format) { - if (!supportsFormat(format)) { - throw LOGGER.logThrowableAsError( - new UnsupportedOperationException("The provided format (" + format + ") is not supported.")); + public ObjectSerializer getSerializerForFormat(SerializationFormat format) { + for (ObjectSerializer serializer : serializers) { + if (serializer.supportsFormat(format)) { + return serializer; + } } + + throw LOGGER.logThrowableAsError( + new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java index 4488a52d45f53..3f12d59b1d473 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java @@ -10,7 +10,6 @@ import io.clientcore.core.serialization.json.JsonWriter; import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.serializer.JsonSerializer; -import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -34,9 +33,7 @@ public DefaultJsonSerializer() { @SuppressWarnings("unchecked") @Override - public T deserializeFromBytes(byte[] bytes, Type type, ObjectSerializer.Format format) throws IOException { - verifyFormat(format); - + public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { try (JsonReader jsonReader = JsonProviders.createReader(bytes)) { if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { Class clazz = (Class) type; @@ -52,10 +49,7 @@ public T deserializeFromBytes(byte[] bytes, Type type, ObjectSerializer.Form @SuppressWarnings("unchecked") @Override - public T deserializeFromStream(InputStream stream, Type type, ObjectSerializer.Format format) - throws IOException { - verifyFormat(format); - + public T deserializeFromStream(InputStream stream, Type type) throws IOException { try (JsonReader jsonReader = JsonProviders.createReader(stream)) { if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { Class clazz = (Class) type; @@ -70,9 +64,7 @@ public T deserializeFromStream(InputStream stream, Type type, ObjectSerializ } @Override - public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) throws IOException { - verifyFormat(format); - + public byte[] serializeToBytes(Object value) throws IOException { if (value == null) { return null; } @@ -92,10 +84,7 @@ public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) thr } @Override - public void serializeToStream(OutputStream stream, Object value, ObjectSerializer.Format format) - throws IOException { - verifyFormat(format); - + public void serializeToStream(OutputStream stream, Object value) throws IOException { if (value == null) { return; } @@ -104,10 +93,4 @@ public void serializeToStream(OutputStream stream, Object value, ObjectSerialize jsonWriter.writeUntyped(value); } } - - private static void verifyFormat(ObjectSerializer.Format format) { - if (format != ObjectSerializer.Format.JSON) { - throw LOGGER.logThrowableAsError(new UnsupportedOperationException("Only JSON format is supported.")); - } - } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java index da4a10e651209..dd5b7c9481fb4 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java @@ -8,7 +8,6 @@ import io.clientcore.core.serialization.xml.XmlSerializable; import io.clientcore.core.serialization.xml.XmlWriter; import io.clientcore.core.util.ClientLogger; -import io.clientcore.core.util.serializer.ObjectSerializer; import io.clientcore.core.util.serializer.XmlSerializer; import javax.xml.stream.XMLStreamException; @@ -33,9 +32,7 @@ public DefaultXmlSerializer() { } @Override - public T deserializeFromBytes(byte[] bytes, Type type, ObjectSerializer.Format format) throws IOException { - verifyFormat(format); - + public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { try (XmlReader xmlReader = XmlReader.fromBytes(bytes)) { return deserializeShared(xmlReader, type); } catch (XMLStreamException ex) { @@ -44,10 +41,7 @@ public T deserializeFromBytes(byte[] bytes, Type type, ObjectSerializer.Form } @Override - public T deserializeFromStream(InputStream stream, Type type, ObjectSerializer.Format format) - throws IOException { - verifyFormat(format); - + public T deserializeFromStream(InputStream stream, Type type) throws IOException { try (XmlReader xmlReader = XmlReader.fromStream(stream)) { return deserializeShared(xmlReader, type); } catch (XMLStreamException ex) { @@ -73,9 +67,7 @@ private static T deserializeShared(XmlReader xmlReader, Type type) { } @Override - public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) throws IOException { - verifyFormat(format); - + public byte[] serializeToBytes(Object value) throws IOException { if (value == null) { return null; } @@ -91,10 +83,7 @@ public byte[] serializeToBytes(Object value, ObjectSerializer.Format format) thr } @Override - public void serializeToStream(OutputStream stream, Object value, ObjectSerializer.Format format) - throws IOException { - verifyFormat(format); - + public void serializeToStream(OutputStream stream, Object value) throws IOException { if (value == null) { return; } @@ -119,10 +108,4 @@ private static void serializeShared(XmlWriter xmlWriter, Object value) { throw LOGGER.logThrowableAsError(new RuntimeException(e)); } } - - private static void verifyFormat(ObjectSerializer.Format format) { - if (format != ObjectSerializer.Format.XML) { - throw LOGGER.logThrowableAsError(new UnsupportedOperationException("Only XML format is supported.")); - } - } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/HttpResponseBodyDecoder.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/HttpResponseBodyDecoder.java index e0fa776fd4664..038d1ec126e27 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/HttpResponseBodyDecoder.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/HttpResponseBodyDecoder.java @@ -8,11 +8,12 @@ import io.clientcore.core.http.models.HttpMethod; import io.clientcore.core.http.models.Response; import io.clientcore.core.implementation.TypeUtil; +import io.clientcore.core.implementation.http.rest.RestProxyImpl; import io.clientcore.core.implementation.util.Base64Uri; import io.clientcore.core.implementation.util.DateTimeRfc1123; import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.SerializationFormat; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -39,13 +40,13 @@ public final class HttpResponseBodyDecoder { * * @param body The response body retrieved from the {@link Response} to decode. * @param response The {@link Response}. - * @param serializer The {@link ObjectSerializer} that performs the decoding. + * @param serializer The {@link CompositeSerializer} that performs the decoding. * @param decodeData The API method metadata used during decoding of the {@link Response response}. * @return The decoded {@link Response response} body, or {@code null} if the body could not be decoded. * @throws HttpResponseException If the body cannot be decoded. * @throws RuntimeException If the body cannot be decoded. */ - public static Object decodeByteArray(BinaryData body, Response response, ObjectSerializer serializer, + public static Object decodeByteArray(BinaryData body, Response response, CompositeSerializer serializer, HttpResponseDecodeData decodeData) { ensureRequestSet(response); @@ -58,7 +59,7 @@ public static Object decodeByteArray(BinaryData body, Response response, Obje try { return deserializeBody(body, decodeData.getUnexpectedException(response.getStatusCode()).getExceptionBodyClass(), null, - serializer); + RestProxyImpl.serializationFormatFromContentType(response.getHeaders()), serializer); } catch (IOException e) { return LOGGER.atWarning().log("Failed to deserialize the error entity.", e); } catch (RuntimeException e) { @@ -90,7 +91,8 @@ public static Object decodeByteArray(BinaryData body, Response response, Obje try { return deserializeBody(body == null ? response.getBody() : body, - extractEntityTypeFromReturnType(decodeData), decodeData.getReturnValueWireType(), serializer); + extractEntityTypeFromReturnType(decodeData), decodeData.getReturnValueWireType(), + RestProxyImpl.serializationFormatFromContentType(response.getHeaders()), serializer); } catch (MalformedValueException e) { throw new HttpResponseException("HTTP response has a malformed body.", response, null, e); } catch (IOException e) { @@ -143,20 +145,21 @@ static boolean isErrorStatus(int statusCode, HttpResponseDecodeData decodeData) * @return Deserialized object. * @throws IOException If the deserialization fails. */ - private static Object deserializeBody(BinaryData value, Type resultType, Type wireType, ObjectSerializer serializer) - throws IOException { + private static Object deserializeBody(BinaryData value, Type resultType, Type wireType, SerializationFormat format, + CompositeSerializer serializer) throws IOException { if (wireType == null) { - return deserialize(value, resultType, serializer); + return deserialize(value, resultType, format, serializer); } else { Type wireResponseType = constructWireResponseType(resultType, wireType); - Object wireResponse = deserialize(value, wireResponseType, serializer); + Object wireResponse = deserialize(value, wireResponseType, format, serializer); return convertToResultType(wireResponse, resultType, wireType); } } - private static Object deserialize(BinaryData value, Type type, ObjectSerializer serializer) throws IOException { - return serializer.deserializeFromBytes(value == null ? EMPTY_BYTE_ARRAY : value.toBytes(), type); + private static Object deserialize(BinaryData value, Type type, SerializationFormat format, + CompositeSerializer serializer) throws IOException { + return serializer.deserializeFromBytes(value == null ? EMPTY_BYTE_ARRAY : value.toBytes(), type, format); } /** diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java index 318927c2d2c5e..b6942ab67cc05 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java @@ -4,7 +4,6 @@ package io.clientcore.core.util.binarydata; import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; -import io.clientcore.core.implementation.http.serializer.DefaultXmlSerializer; import io.clientcore.core.serialization.json.JsonWriter; import io.clientcore.core.util.serializer.ObjectSerializer; @@ -21,7 +20,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.util.Arrays; import java.util.List; /** @@ -120,8 +118,7 @@ public abstract class BinaryData implements Closeable { private static final BinaryData EMPTY = BinaryData.fromBytes(new byte[0]); - static final ObjectSerializer SERIALIZER - = ObjectSerializer.compositeSerializer(Arrays.asList(new DefaultJsonSerializer(), new DefaultXmlSerializer())); + static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); static final int STREAM_READ_SIZE = 8192; static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; static final String TOO_LARGE_FOR_BYTE_ARRAY diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java index 6f499cf002862..1eb6d47fd5e3e 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java @@ -23,55 +23,47 @@ public JsonSerializer() { * * @param data The JSON byte array. * @param type {@link Type} representing the object. - * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized JSON byte array. - * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the deserialization fails. */ @Override - public abstract T deserializeFromBytes(byte[] data, Type type, Format format) throws IOException; + public abstract T deserializeFromBytes(byte[] data, Type type) throws IOException; /** * Reads a JSON stream into its object representation. * * @param stream JSON stream. * @param type {@link Type} representing the object. - * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized JSON stream. - * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the deserialization fails. */ @Override - public abstract T deserializeFromStream(InputStream stream, Type type, Format format) throws IOException; + public abstract T deserializeFromStream(InputStream stream, Type type) throws IOException; /** * Converts the object into a JSON byte array. * * @param value The object. - * @param format The format to deserialize the object from. * @return The JSON binary representation of the serialized object. - * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the serialization fails. */ @Override - public abstract byte[] serializeToBytes(Object value, Format format) throws IOException; + public abstract byte[] serializeToBytes(Object value) throws IOException; /** * Writes an object's JSON representation into a stream. * * @param stream {@link OutputStream} where the object's JSON representation will be written. * @param value The object to serialize. - * @param format The format to deserialize the object from. - * @throws UnsupportedOperationException If the provided format is not {@link Format#JSON}. * @throws IOException If the serialization fails. */ @Override - public abstract void serializeToStream(OutputStream stream, Object value, Format format) throws IOException; + public abstract void serializeToStream(OutputStream stream, Object value) throws IOException; @Override - public final boolean supportsFormat(Format format) { - return format == Format.JSON; + public final boolean supportsFormat(SerializationFormat format) { + return format == SerializationFormat.JSON; } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java index 886fad22cbdf6..ff3bdb4e09355 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java @@ -3,13 +3,10 @@ package io.clientcore.core.util.serializer; -import io.clientcore.core.implementation.http.serializer.CompositeSerializer; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Type; -import java.util.List; /** * Generic interface covering serializing and deserialization objects. @@ -21,77 +18,45 @@ public abstract class ObjectSerializer { public ObjectSerializer() { } - /** - * Creates an instance of {@link ObjectSerializer} which comprises multiple serializers. - * - * @param serializers The serializers. - * @return An instance of {@link ObjectSerializer}. - * @throws NullPointerException If {@code serializers} is null. - * @throws IllegalArgumentException If {@code serializers} is empty. - */ - public static ObjectSerializer compositeSerializer(List serializers) { - return new CompositeSerializer(serializers); - } - /** * Reads a byte array into its object representation. - *

- * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an - * {@link UnsupportedOperationException} will be thrown. * * @param data The byte array. * @param type {@link Type} representing the object. - * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized byte array. - * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the deserialization fails. */ - public abstract T deserializeFromBytes(byte[] data, Type type, Format format) throws IOException; + public abstract T deserializeFromBytes(byte[] data, Type type) throws IOException; /** * Reads a stream into its object representation. - *

- * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an - * {@link UnsupportedOperationException} will be thrown. * * @param stream {@link InputStream} of data. * @param type {@link Type} representing the object. - * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized stream. - * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the deserialization fails. */ - public abstract T deserializeFromStream(InputStream stream, Type type, Format format) throws IOException; + public abstract T deserializeFromStream(InputStream stream, Type type) throws IOException; /** * Serializes an object into a byte array. - *

- * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an - * {@link UnsupportedOperationException} will be thrown. * * @param value The object to serialize. - * @param format The format to serialize the object in. * @return The binary representation of the serialized object. - * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the serialization fails. */ - public abstract byte[] serializeToBytes(Object value, Format format) throws IOException; + public abstract byte[] serializeToBytes(Object value) throws IOException; /** * Serializes and writes an object into a provided stream. - *

- * If the {@link ObjectSerializer#supportsFormat(Format)} doesn't support the provided {@link Format}, an - * {@link UnsupportedOperationException} will be thrown. * * @param stream {@link OutputStream} where the serialized object will be written. * @param value The object to serialize. - * @param format The format to serialize the object in. - * @throws UnsupportedOperationException If the provided format is not supported. * @throws IOException If the serialization fails. */ - public abstract void serializeToStream(OutputStream stream, Object value, Format format) throws IOException; + public abstract void serializeToStream(OutputStream stream, Object value) throws IOException; /** * Indicates whether the given implementation of {@link ObjectSerializer} supports the provided format. @@ -103,25 +68,5 @@ public static ObjectSerializer compositeSerializer(List serial * @param format The format to check support for. * @return Whether the format is supported. */ - public abstract boolean supportsFormat(Format format); - - /** - * Formats supported by {@link ObjectSerializer}, and its subclasses. - */ - public enum Format { - /** - * JSON format. - */ - JSON, - - /** - * Text format. - */ - TEXT, - - /** - * XML format. - */ - XML - } + public abstract boolean supportsFormat(SerializationFormat format); } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/SerializationFormat.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/SerializationFormat.java new file mode 100644 index 0000000000000..f24d91ee17251 --- /dev/null +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/SerializationFormat.java @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package io.clientcore.core.util.serializer; + +/** + * Represents serialization formats. + */ +public enum SerializationFormat { + /** + * JSON serialization format. + */ + JSON, + + /** + * XML serialization format. + */ + TEXT, + + /** + * XML serialization format. + */ + XML; +} diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java index e0f669ecbab8e..d43f7c517c8fd 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java @@ -23,55 +23,47 @@ public XmlSerializer() { * * @param data The XML byte array. * @param type {@link Type} representing the object. - * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized XML byte array. - * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. * @throws IOException If the deserialization fails. */ @Override - public abstract T deserializeFromBytes(byte[] data, Type type, Format format) throws IOException; + public abstract T deserializeFromBytes(byte[] data, Type type) throws IOException; /** * Reads an XML stream into its object representation. * * @param stream XML stream. * @param type {@link Type} representing the object. - * @param format The format to deserialize the object from. * @param Type of the object. * @return The object represented by the deserialized XML stream. - * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. * @throws IOException If the deserialization fails. */ @Override - public abstract T deserializeFromStream(InputStream stream, Type type, Format format) throws IOException; + public abstract T deserializeFromStream(InputStream stream, Type type) throws IOException; /** * Converts the object into an XML byte array. * * @param value The object. - * @param format The format to deserialize the object from. * @return The XML binary representation of the serialized object. - * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. * @throws IOException If the serialization fails. */ @Override - public abstract byte[] serializeToBytes(Object value, Format format) throws IOException; + public abstract byte[] serializeToBytes(Object value) throws IOException; /** * Writes an object's XML representation into a stream. * * @param stream {@link OutputStream} where the object's XML representation will be written. * @param value The object to serialize. - * @param format The format to deserialize the object from. - * @throws UnsupportedOperationException If the provided format is not {@link Format#XML}. * @throws IOException If the serialization fails. */ @Override - public abstract void serializeToStream(OutputStream stream, Object value, Format format) throws IOException; + public abstract void serializeToStream(OutputStream stream, Object value) throws IOException; @Override - public final boolean supportsFormat(Format format) { - return format == Format.XML; + public final boolean supportsFormat(SerializationFormat format) { + return format == SerializationFormat.XML; } } diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java index 12ceb9fcf60fd..7e18022e2524a 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java @@ -21,6 +21,7 @@ import io.clientcore.core.http.models.RequestOptions; import io.clientcore.core.http.models.Response; import io.clientcore.core.implementation.TypeUtil; +import io.clientcore.core.implementation.http.serializer.CompositeSerializer; import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.util.Base64Uri; import io.clientcore.core.implementation.util.DateTimeRfc1123; @@ -28,7 +29,6 @@ import io.clientcore.core.models.SimpleClass; import io.clientcore.core.util.Context; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -57,7 +57,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class SwaggerMethodParserTests { - private static final ObjectSerializer DEFAULT_SERIALIZER = new DefaultJsonSerializer(); + private static final CompositeSerializer DEFAULT_SERIALIZER + = new CompositeSerializer(Arrays.asList(new DefaultJsonSerializer())); @ServiceInterface(name = "OperationMethods", host = "https://raw.host.com") interface OperationMethods { diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java index 612b8b1360755..c110fe69d1554 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java @@ -11,13 +11,13 @@ import io.clientcore.core.http.models.HttpRequest; import io.clientcore.core.http.models.Response; import io.clientcore.core.implementation.http.UnexpectedExceptionInformation; +import io.clientcore.core.implementation.http.serializer.CompositeSerializer; import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.http.serializer.HttpResponseBodyDecoder; import io.clientcore.core.implementation.http.serializer.HttpResponseDecodeData; import io.clientcore.core.implementation.util.Base64Uri; import io.clientcore.core.implementation.util.DateTimeRfc1123; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -45,7 +45,8 @@ * Tests {@link HttpResponseBodyDecoder}. */ public class HttpResponseBodyDecoderTests { - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + private static final CompositeSerializer SERIALIZER + = new CompositeSerializer(Arrays.asList(new DefaultJsonSerializer())); private static final HttpRequest GET_REQUEST = new HttpRequest(HttpMethod.GET, "https://localhost"); private static final HttpRequest HEAD_REQUEST = new HttpRequest(HttpMethod.HEAD, "https://localhost"); @@ -99,12 +100,12 @@ private static Stream errorResponseSupplier() { @Test public void exceptionInErrorDeserializationReturnsException() { - ObjectSerializer ioExceptionThrower = new DefaultJsonSerializer() { + CompositeSerializer ioExceptionThrower = new CompositeSerializer(Arrays.asList(new DefaultJsonSerializer() { @Override public T deserializeFromBytes(byte[] bytes, Type type) { throw new UncheckedIOException(new IOException()); } - }; + })); HttpResponseDecodeData noExpectedStatusCodes = new MockHttpResponseDecodeData(new UnexpectedExceptionInformation(null, null)); diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java index 41074f6774686..c0f246ec374bc 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java @@ -38,6 +38,7 @@ import io.clientcore.core.util.binarydata.ByteBufferBinaryData; import io.clientcore.core.util.binarydata.InputStreamBinaryData; import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.SerializationFormat; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; @@ -406,7 +407,7 @@ protected URI getRequestUri(String requestPath) { } } - private static class ByteArraySerializer implements ObjectSerializer { + private static class ByteArraySerializer extends ObjectSerializer { @Override public T deserializeFromBytes(byte[] data, Type type) { return null; @@ -430,6 +431,11 @@ public void serializeToStream(OutputStream stream, Object value) { throw new RuntimeException(e); } } + + @Override + public boolean supportsFormat(SerializationFormat format) { + return false; + } } @ServiceInterface(name = "Service1", host = "{uri}") diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java index 1a22dcce5f63c..de6f9abfbbaae 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java @@ -7,7 +7,7 @@ import java.io.OutputStream; import java.lang.reflect.Type; -public class MockSerializer implements JsonSerializer { +public class MockSerializer extends JsonSerializer { @Override public T deserializeFromBytes(byte[] data, Type type) { return null; From 3a64e2bce20972ac8c0f4744494aef311670dc01 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:19:52 -0500 Subject: [PATCH 3/6] Fix up CI --- .../core/checkstyle-suppressions.xml | 3 -- sdk/clientcore/core/spotbugs-exclude.xml | 3 ++ .../io/clientcore/core/http/RestProxy.java | 1 - .../http/serializer/CompositeSerializer.java | 49 +++++++++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/sdk/clientcore/core/checkstyle-suppressions.xml b/sdk/clientcore/core/checkstyle-suppressions.xml index 12aa0afbb219e..54063f5ebb41a 100644 --- a/sdk/clientcore/core/checkstyle-suppressions.xml +++ b/sdk/clientcore/core/checkstyle-suppressions.xml @@ -4,7 +4,6 @@ - @@ -15,14 +14,12 @@ - - diff --git a/sdk/clientcore/core/spotbugs-exclude.xml b/sdk/clientcore/core/spotbugs-exclude.xml index 9d6a0fb803ed3..9a8fc980073fc 100644 --- a/sdk/clientcore/core/spotbugs-exclude.xml +++ b/sdk/clientcore/core/spotbugs-exclude.xml @@ -28,7 +28,9 @@ + + @@ -257,6 +259,7 @@ + diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java index 056abef4e8f37..888862cd58e00 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java @@ -6,7 +6,6 @@ import io.clientcore.core.http.models.RequestOptions; import io.clientcore.core.http.pipeline.HttpPipeline; import io.clientcore.core.implementation.http.rest.RestProxyImpl; -import io.clientcore.core.implementation.http.rest.RestProxyUtils; import io.clientcore.core.implementation.http.rest.SwaggerInterfaceParser; import io.clientcore.core.implementation.http.rest.SwaggerMethodParser; import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java index b56a5395858ec..4daabb084f64a 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java @@ -27,6 +27,8 @@ public final class CompositeSerializer { * Creates an instance of the {@link CompositeSerializer}. * * @param serializers The list of serializers to try in order. + * @throws NullPointerException If the list of serializers is null. + * @throws IllegalArgumentException If the list of serializers is empty. */ public CompositeSerializer(List serializers) { Objects.requireNonNull(serializers, "The list of serializers cannot be null."); @@ -37,6 +39,17 @@ public CompositeSerializer(List serializers) { this.serializers = new ArrayList<>(serializers); } + /** + * Deserializes the provided data into an object of the provided type. + * + * @param data The data to deserialize. + * @param type The type of the object to deserialize. + * @param format The format of the data. + * @return The deserialized object. + * @param The type of the object to deserialize. + * @throws UnsupportedOperationException If none of the provided serializers support the provided format. + * @throws IOException If an error occurs during deserialization. + */ public T deserializeFromBytes(byte[] data, Type type, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { @@ -48,6 +61,17 @@ public T deserializeFromBytes(byte[] data, Type type, SerializationFormat fo new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } + /** + * Deserializes the provided stream into an object of the provided type. + * + * @param stream The stream to deserialize. + * @param type The type of the object to deserialize. + * @param format The format of the data. + * @return The deserialized object. + * @param The type of the object to deserialize. + * @throws UnsupportedOperationException If none of the provided serializers support the provided format. + * @throws IOException If an error occurs during deserialization. + */ public T deserializeFromStream(InputStream stream, Type type, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { @@ -59,6 +83,15 @@ public T deserializeFromStream(InputStream stream, Type type, SerializationF new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } + /** + * Serializes the provided value into a byte array. + * + * @param value The value to serialize. + * @param format The format to serialize the value in. + * @return The serialized byte array. + * @throws UnsupportedOperationException If none of the provided serializers support the provided format. + * @throws IOException If an error occurs during serialization. + */ public byte[] serializeToBytes(Object value, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { @@ -70,6 +103,15 @@ public byte[] serializeToBytes(Object value, SerializationFormat format) throws new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } + /** + * Serializes the provided value into the given stream. + * + * @param stream The stream to serialize the value to. + * @param value The value to serialize. + * @param format The format to serialize the value in. + * @throws UnsupportedOperationException If none of the provided serializers support the provided format. + * @throws IOException If an error occurs during serialization. + */ public void serializeToStream(OutputStream stream, Object value, SerializationFormat format) throws IOException { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { @@ -82,6 +124,13 @@ public void serializeToStream(OutputStream stream, Object value, SerializationFo new UnsupportedOperationException("None of the provided serializers support the format: " + format + ".")); } + /** + * Gets the first serializer that supports the provided format. + * + * @param format The format to get the serializer for. + * @return The serializer that supports the provided format. + * @throws UnsupportedOperationException If none of the provided serializers support the provided format. + */ public ObjectSerializer getSerializerForFormat(SerializationFormat format) { for (ObjectSerializer serializer : serializers) { if (serializer.supportsFormat(format)) { From 7beea7d8e4038f7d1d443092e9e9646e919595c5 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:44:44 -0500 Subject: [PATCH 4/6] Make JsonSerializer and XmlSerializer the default implementations themselves --- .../io/clientcore/core/http/RestProxy.java | 6 +- .../http/serializer/CompositeSerializer.java | 2 +- .../serializer/DefaultJsonSerializer.java | 96 --------------- .../http/serializer/DefaultXmlSerializer.java | 111 ------------------ .../core/util/binarydata/BinaryData.java | 4 +- .../core/util/serializer/JsonSerializer.java | 85 ++++++++++++-- .../util/serializer/ObjectSerializer.java | 18 +-- .../core/util/serializer/XmlSerializer.java | 98 ++++++++++++++-- .../core/http/MockHttpResponse.java | 4 +- .../clientcore/core/http/RestProxyTests.java | 20 ++-- ...nseConstructorsCacheBenchMarkTestData.java | 4 +- .../http/rest/RestProxyImplTests.java | 6 +- .../http/rest/SwaggerMethodParserTests.java | 4 +- .../AdditionalPropertiesSerializerTests.java | 12 +- .../BinaryDataSerializationTests.java | 3 +- .../HttpResponseBodyDecoderTests.java | 6 +- .../JsonSerializableEndToEndTests.java | 7 +- .../core/shared/HttpClientTests.java | 32 ++--- .../core/shared/HttpClientTestsServer.java | 4 +- .../core/util/binarydata/BinaryDataTest.java | 8 +- ...zerTests.java => JsonSerializerTests.java} | 10 +- 21 files changed, 239 insertions(+), 301 deletions(-) delete mode 100644 sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java delete mode 100644 sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java rename sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/{DefaultJsonSerializerTests.java => JsonSerializerTests.java} (96%) diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java index 888862cd58e00..0faa9cdf37c2e 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java @@ -8,9 +8,9 @@ import io.clientcore.core.implementation.http.rest.RestProxyImpl; import io.clientcore.core.implementation.http.rest.SwaggerInterfaceParser; import io.clientcore.core.implementation.http.rest.SwaggerMethodParser; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; -import io.clientcore.core.implementation.http.serializer.DefaultXmlSerializer; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.XmlSerializer; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -74,7 +74,7 @@ public Object invoke(Object proxy, final Method method, Object[] args) { public static A create(Class swaggerInterface, HttpPipeline httpPipeline) { final SwaggerInterfaceParser interfaceParser = SwaggerInterfaceParser.getInstance(swaggerInterface); final RestProxy restProxy - = new RestProxy(httpPipeline, interfaceParser, new DefaultJsonSerializer(), new DefaultXmlSerializer()); + = new RestProxy(httpPipeline, interfaceParser, new JsonSerializer(), new XmlSerializer()); return (A) Proxy.newProxyInstance(swaggerInterface.getClassLoader(), new Class[] { swaggerInterface }, restProxy); diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java index 4daabb084f64a..b5008ae5bec3a 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/CompositeSerializer.java @@ -16,7 +16,7 @@ /** * An internal type that comprises multiple {@link ObjectSerializer}s and adds functionality to determine which - * {@link SerializationFormat} to use each serialization and deserialize operation. + * {@link SerializationFormat} to use for each serialization and deserialize operation. */ public final class CompositeSerializer { private static final ClientLogger LOGGER = new ClientLogger(CompositeSerializer.class); diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java deleted file mode 100644 index 3f12d59b1d473..0000000000000 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultJsonSerializer.java +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package io.clientcore.core.implementation.http.serializer; - -import io.clientcore.core.implementation.TypeUtil; -import io.clientcore.core.serialization.json.JsonProviders; -import io.clientcore.core.serialization.json.JsonReader; -import io.clientcore.core.serialization.json.JsonSerializable; -import io.clientcore.core.serialization.json.JsonWriter; -import io.clientcore.core.util.ClientLogger; -import io.clientcore.core.util.serializer.JsonSerializer; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; - -/** - * Default implementation of the {@link JsonSerializer}. - */ -public class DefaultJsonSerializer extends JsonSerializer { - // DefaultJsonSerializer is a commonly used class, use a static logger. - private static final ClientLogger LOGGER = new ClientLogger(DefaultJsonSerializer.class); - - /** - * Creates an instance of the {@link DefaultJsonSerializer}. - */ - public DefaultJsonSerializer() { - } - - @SuppressWarnings("unchecked") - @Override - public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { - try (JsonReader jsonReader = JsonProviders.createReader(bytes)) { - if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { - Class clazz = (Class) type; - - return (T) clazz.getMethod("fromJson", JsonReader.class).invoke(null, jsonReader); - } else { - return (T) jsonReader.readUntyped(); - } - } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { - throw LOGGER.logThrowableAsError(new RuntimeException(e)); - } - } - - @SuppressWarnings("unchecked") - @Override - public T deserializeFromStream(InputStream stream, Type type) throws IOException { - try (JsonReader jsonReader = JsonProviders.createReader(stream)) { - if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { - Class clazz = (Class) type; - - return (T) clazz.getMethod("fromJson", JsonReader.class).invoke(null, jsonReader); - } else { - return (T) jsonReader.readUntyped(); - } - } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { - throw LOGGER.logThrowableAsError(new RuntimeException(e)); - } - } - - @Override - public byte[] serializeToBytes(Object value) throws IOException { - if (value == null) { - return null; - } - - if (value instanceof JsonSerializable) { - return ((JsonSerializable) value).toJsonBytes(); - } - - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - JsonWriter jsonWriter = JsonProviders.createWriter(byteArrayOutputStream)) { - - jsonWriter.writeUntyped(value); - jsonWriter.flush(); - - return byteArrayOutputStream.toByteArray(); - } - } - - @Override - public void serializeToStream(OutputStream stream, Object value) throws IOException { - if (value == null) { - return; - } - - try (JsonWriter jsonWriter = JsonProviders.createWriter(stream)) { - jsonWriter.writeUntyped(value); - } - } -} diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java deleted file mode 100644 index dd5b7c9481fb4..0000000000000 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/serializer/DefaultXmlSerializer.java +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package io.clientcore.core.implementation.http.serializer; - -import io.clientcore.core.implementation.TypeUtil; -import io.clientcore.core.serialization.xml.XmlReader; -import io.clientcore.core.serialization.xml.XmlSerializable; -import io.clientcore.core.serialization.xml.XmlWriter; -import io.clientcore.core.util.ClientLogger; -import io.clientcore.core.util.serializer.XmlSerializer; - -import javax.xml.stream.XMLStreamException; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; - -/** - * Default implementation of the {@link XmlSerializer}. - */ -public class DefaultXmlSerializer extends XmlSerializer { - // DefaultXmlSerializer is a commonly used class, use a static logger. - private static final ClientLogger LOGGER = new ClientLogger(DefaultXmlSerializer.class); - - /** - * Creates an instance of the {@link DefaultXmlSerializer}. - */ - public DefaultXmlSerializer() { - } - - @Override - public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { - try (XmlReader xmlReader = XmlReader.fromBytes(bytes)) { - return deserializeShared(xmlReader, type); - } catch (XMLStreamException ex) { - throw LOGGER.logThrowableAsError(new IOException(ex)); - } - } - - @Override - public T deserializeFromStream(InputStream stream, Type type) throws IOException { - try (XmlReader xmlReader = XmlReader.fromStream(stream)) { - return deserializeShared(xmlReader, type); - } catch (XMLStreamException ex) { - throw LOGGER.logThrowableAsError(new IOException(ex)); - } - } - - @SuppressWarnings("unchecked") - private static T deserializeShared(XmlReader xmlReader, Type type) { - try { - if (type instanceof Class && XmlSerializer.class.isAssignableFrom(TypeUtil.getRawClass(type))) { - Class clazz = (Class) type; - - return (T) clazz.getMethod("fromXml", XmlReader.class).invoke(null, xmlReader); - } else { - // TODO (alzimmer): XML needs untyped support. - throw LOGGER.logThrowableAsError(new UnsupportedOperationException( - "DefaultXmlSerializer does not have support for untyped deserialization.")); - } - } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { - throw LOGGER.logThrowableAsError(new RuntimeException(e)); - } - } - - @Override - public byte[] serializeToBytes(Object value) throws IOException { - if (value == null) { - return null; - } - - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - XmlWriter xmlWriter = XmlWriter.toStream(byteArrayOutputStream)) { - serializeShared(xmlWriter, value); - - return byteArrayOutputStream.toByteArray(); - } catch (XMLStreamException ex) { - throw LOGGER.logThrowableAsError(new IOException(ex)); - } - } - - @Override - public void serializeToStream(OutputStream stream, Object value) throws IOException { - if (value == null) { - return; - } - - try (XmlWriter xmlWriter = XmlWriter.toStream(stream)) { - serializeShared(xmlWriter, value); - } catch (XMLStreamException ex) { - throw LOGGER.logThrowableAsError(new IOException(ex)); - } - } - - private static void serializeShared(XmlWriter xmlWriter, Object value) { - try { - if (value instanceof XmlSerializable) { - ((XmlSerializable) value).toXml(xmlWriter).flush(); - } else { - // TODO (alzimmer): XML needs untyped support. - throw LOGGER.logThrowableAsError(new UnsupportedOperationException( - "DefaultXmlSerializer does not have support for untyped serialization.")); - } - } catch (XMLStreamException e) { - throw LOGGER.logThrowableAsError(new RuntimeException(e)); - } - } -} diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java index b6942ab67cc05..11770d532e926 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java @@ -3,8 +3,8 @@ package io.clientcore.core.util.binarydata; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.serialization.json.JsonWriter; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.Closeable; @@ -118,7 +118,7 @@ public abstract class BinaryData implements Closeable { private static final BinaryData EMPTY = BinaryData.fromBytes(new byte[0]); - static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + static final ObjectSerializer SERIALIZER = new JsonSerializer(); static final int STREAM_READ_SIZE = 8192; static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; static final String TOO_LARGE_FOR_BYTE_ARRAY diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java index 1eb6d47fd5e3e..34d513b7c23f2 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java @@ -3,15 +3,35 @@ package io.clientcore.core.util.serializer; +import io.clientcore.core.implementation.TypeUtil; +import io.clientcore.core.serialization.json.JsonProviders; +import io.clientcore.core.serialization.json.JsonReader; +import io.clientcore.core.serialization.json.JsonSerializable; +import io.clientcore.core.serialization.json.JsonWriter; +import io.clientcore.core.util.ClientLogger; + +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; /** - * Generic interface covering basic JSON serialization and deserialization methods. + * Class providing basic JSON serialization and deserialization methods. + *

+ * The implementation of this class is based on the usage of {@link JsonReader} and {@link JsonWriter}. + *

+ * The deserialization methods only work with primitive types, simple list and map collections, and models implementing + * {@link JsonSerializable}. Or, in code terms, types that are producible calling {@link JsonReader#readUntyped()} or + * provide a static factory method {@code fromJson(JsonReader)}. + *

+ * The serialization methods will work with any value but for complex types that don't implement + * {@link JsonSerializable} they will serialize the object using the type's {@code toString()} method. */ -public abstract class JsonSerializer extends ObjectSerializer { +public class JsonSerializer implements ObjectSerializer { + private static final ClientLogger LOGGER = new ClientLogger(JsonSerializer.class); + /** * Creates an instance of the {@link JsonSerializer}. */ @@ -21,14 +41,27 @@ public JsonSerializer() { /** * Reads a JSON byte array into its object representation. * - * @param data The JSON byte array. + * @param bytes The JSON byte array. * @param type {@link Type} representing the object. * @param Type of the object. * @return The object represented by the deserialized JSON byte array. * @throws IOException If the deserialization fails. */ + @SuppressWarnings("unchecked") @Override - public abstract T deserializeFromBytes(byte[] data, Type type) throws IOException; + public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { + try (JsonReader jsonReader = JsonProviders.createReader(bytes)) { + if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { + Class clazz = (Class) type; + + return (T) clazz.getMethod("fromJson", JsonReader.class).invoke(null, jsonReader); + } else { + return (T) jsonReader.readUntyped(); + } + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + throw LOGGER.logThrowableAsError(new RuntimeException(e)); + } + } /** * Reads a JSON stream into its object representation. @@ -39,8 +72,21 @@ public JsonSerializer() { * @return The object represented by the deserialized JSON stream. * @throws IOException If the deserialization fails. */ + @SuppressWarnings("unchecked") @Override - public abstract T deserializeFromStream(InputStream stream, Type type) throws IOException; + public T deserializeFromStream(InputStream stream, Type type) throws IOException { + try (JsonReader jsonReader = JsonProviders.createReader(stream)) { + if (type instanceof Class && JsonSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { + Class clazz = (Class) type; + + return (T) clazz.getMethod("fromJson", JsonReader.class).invoke(null, jsonReader); + } else { + return (T) jsonReader.readUntyped(); + } + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + throw LOGGER.logThrowableAsError(new RuntimeException(e)); + } + } /** * Converts the object into a JSON byte array. @@ -50,7 +96,24 @@ public JsonSerializer() { * @throws IOException If the serialization fails. */ @Override - public abstract byte[] serializeToBytes(Object value) throws IOException; + public byte[] serializeToBytes(Object value) throws IOException { + if (value == null) { + return null; + } + + if (value instanceof JsonSerializable) { + return ((JsonSerializable) value).toJsonBytes(); + } + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + JsonWriter jsonWriter = JsonProviders.createWriter(byteArrayOutputStream)) { + + jsonWriter.writeUntyped(value); + jsonWriter.flush(); + + return byteArrayOutputStream.toByteArray(); + } + } /** * Writes an object's JSON representation into a stream. @@ -60,7 +123,15 @@ public JsonSerializer() { * @throws IOException If the serialization fails. */ @Override - public abstract void serializeToStream(OutputStream stream, Object value) throws IOException; + public void serializeToStream(OutputStream stream, Object value) throws IOException { + if (value == null) { + return; + } + + try (JsonWriter jsonWriter = JsonProviders.createWriter(stream)) { + jsonWriter.writeUntyped(value); + } + } @Override public final boolean supportsFormat(SerializationFormat format) { diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java index ff3bdb4e09355..d9320d7b6acff 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/ObjectSerializer.java @@ -11,13 +11,7 @@ /** * Generic interface covering serializing and deserialization objects. */ -public abstract class ObjectSerializer { - /** - * Creates an instance of {@link ObjectSerializer}. - */ - public ObjectSerializer() { - } - +public interface ObjectSerializer { /** * Reads a byte array into its object representation. * @@ -27,7 +21,7 @@ public ObjectSerializer() { * @return The object represented by the deserialized byte array. * @throws IOException If the deserialization fails. */ - public abstract T deserializeFromBytes(byte[] data, Type type) throws IOException; + T deserializeFromBytes(byte[] data, Type type) throws IOException; /** * Reads a stream into its object representation. @@ -38,7 +32,7 @@ public ObjectSerializer() { * @return The object represented by the deserialized stream. * @throws IOException If the deserialization fails. */ - public abstract T deserializeFromStream(InputStream stream, Type type) throws IOException; + T deserializeFromStream(InputStream stream, Type type) throws IOException; /** * Serializes an object into a byte array. @@ -47,7 +41,7 @@ public ObjectSerializer() { * @return The binary representation of the serialized object. * @throws IOException If the serialization fails. */ - public abstract byte[] serializeToBytes(Object value) throws IOException; + byte[] serializeToBytes(Object value) throws IOException; /** * Serializes and writes an object into a provided stream. @@ -56,7 +50,7 @@ public ObjectSerializer() { * @param value The object to serialize. * @throws IOException If the serialization fails. */ - public abstract void serializeToStream(OutputStream stream, Object value) throws IOException; + void serializeToStream(OutputStream stream, Object value) throws IOException; /** * Indicates whether the given implementation of {@link ObjectSerializer} supports the provided format. @@ -68,5 +62,5 @@ public ObjectSerializer() { * @param format The format to check support for. * @return Whether the format is supported. */ - public abstract boolean supportsFormat(SerializationFormat format); + boolean supportsFormat(SerializationFormat format); } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java index d43f7c517c8fd..779a3d2e7ffe3 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java @@ -3,15 +3,33 @@ package io.clientcore.core.util.serializer; +import io.clientcore.core.implementation.TypeUtil; +import io.clientcore.core.serialization.xml.XmlReader; +import io.clientcore.core.serialization.xml.XmlSerializable; +import io.clientcore.core.serialization.xml.XmlWriter; +import io.clientcore.core.util.ClientLogger; + +import javax.xml.stream.XMLStreamException; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; /** - * Generic interface covering basic XML serialization and deserialization methods. + * Class providing basic XML serialization and deserialization methods. + *

+ * The implementation of this class is based on the usage of {@link XmlReader} and {@link XmlWriter}. + *

+ * The deserialization methods only work with models implementing {@link XmlSerializable}. Or, in code terms, types that + * provide a static factory method {@code fromXml(XmlReader)}. + *

+ * The serialization methods only work with complex types that implement {@link XmlSerializable}. */ -public abstract class XmlSerializer extends ObjectSerializer { +public class XmlSerializer implements ObjectSerializer { + private static final ClientLogger LOGGER = new ClientLogger(XmlSerializer.class); + /** * Creates an instance of the {@link XmlSerializer}. */ @@ -21,14 +39,20 @@ public XmlSerializer() { /** * Reads an XML byte array into its object representation. * - * @param data The XML byte array. + * @param bytes The XML byte array. * @param type {@link Type} representing the object. * @param Type of the object. * @return The object represented by the deserialized XML byte array. * @throws IOException If the deserialization fails. */ @Override - public abstract T deserializeFromBytes(byte[] data, Type type) throws IOException; + public T deserializeFromBytes(byte[] bytes, Type type) throws IOException { + try (XmlReader xmlReader = XmlReader.fromBytes(bytes)) { + return deserializeShared(xmlReader, type); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } /** * Reads an XML stream into its object representation. @@ -40,7 +64,30 @@ public XmlSerializer() { * @throws IOException If the deserialization fails. */ @Override - public abstract T deserializeFromStream(InputStream stream, Type type) throws IOException; + public T deserializeFromStream(InputStream stream, Type type) throws IOException { + try (XmlReader xmlReader = XmlReader.fromStream(stream)) { + return deserializeShared(xmlReader, type); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } + + @SuppressWarnings("unchecked") + private static T deserializeShared(XmlReader xmlReader, Type type) { + try { + if (type instanceof Class && XmlSerializer.class.isAssignableFrom(TypeUtil.getRawClass(type))) { + Class clazz = (Class) type; + + return (T) clazz.getMethod("fromXml", XmlReader.class).invoke(null, xmlReader); + } else { + // TODO (alzimmer): XML needs untyped support. + throw LOGGER.logThrowableAsError(new UnsupportedOperationException( + "DefaultXmlSerializer does not have support for untyped deserialization.")); + } + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + throw LOGGER.logThrowableAsError(new RuntimeException(e)); + } + } /** * Converts the object into an XML byte array. @@ -50,7 +97,20 @@ public XmlSerializer() { * @throws IOException If the serialization fails. */ @Override - public abstract byte[] serializeToBytes(Object value) throws IOException; + public byte[] serializeToBytes(Object value) throws IOException { + if (value == null) { + return null; + } + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + XmlWriter xmlWriter = XmlWriter.toStream(byteArrayOutputStream)) { + serializeShared(xmlWriter, value); + + return byteArrayOutputStream.toByteArray(); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } /** * Writes an object's XML representation into a stream. @@ -60,7 +120,31 @@ public XmlSerializer() { * @throws IOException If the serialization fails. */ @Override - public abstract void serializeToStream(OutputStream stream, Object value) throws IOException; + public void serializeToStream(OutputStream stream, Object value) throws IOException { + if (value == null) { + return; + } + + try (XmlWriter xmlWriter = XmlWriter.toStream(stream)) { + serializeShared(xmlWriter, value); + } catch (XMLStreamException ex) { + throw LOGGER.logThrowableAsError(new IOException(ex)); + } + } + + private static void serializeShared(XmlWriter xmlWriter, Object value) { + try { + if (value instanceof XmlSerializable) { + ((XmlSerializable) value).toXml(xmlWriter).flush(); + } else { + // TODO (alzimmer): XML needs untyped support. + throw LOGGER.logThrowableAsError(new UnsupportedOperationException( + "DefaultXmlSerializer does not have support for untyped serialization.")); + } + } catch (XMLStreamException e) { + throw LOGGER.logThrowableAsError(new RuntimeException(e)); + } + } @Override public final boolean supportsFormat(SerializationFormat format) { diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java index ec33b977e931e..fac2915755a99 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java @@ -6,12 +6,12 @@ import io.clientcore.core.http.models.HttpHeaders; import io.clientcore.core.http.models.HttpRequest; import io.clientcore.core.http.models.HttpResponse; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.util.binarydata.BinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; public class MockHttpResponse extends HttpResponse { - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + private static final ObjectSerializer SERIALIZER = new JsonSerializer(); /** * Creates an HTTP response associated with a {@code request}, returns the {@code statusCode}, and has an empty diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java index 14c2cc82aae63..1bdbfba9f883d 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java @@ -16,8 +16,8 @@ import io.clientcore.core.http.models.Response; import io.clientcore.core.http.pipeline.HttpPipeline; import io.clientcore.core.http.pipeline.HttpPipelineBuilder; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.util.binarydata.BinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -71,7 +71,7 @@ public void contentTypeHeaderPriorityOverBodyParamAnnotationTest() throws IOExce HttpClient client = new LocalHttpClient(); HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(client).build(); - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); byte[] bytes = "hello".getBytes(); try (Response response = testInterface.testMethod(ByteBuffer.wrap(bytes), "application/json", (long) bytes.length)) { @@ -86,12 +86,12 @@ public void streamResponseShouldHaveHttpResponseReference() { HttpPipeline pipeline = new HttpPipelineBuilder() .httpClient(client) .build(); - - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); StreamResponse streamResponse = testInterface.testDownload(); - + streamResponse.close(); - + // This indirectly tests that StreamResponse has HttpResponse reference. assertTrue(client.closeCalledOnResponse); }*/ @@ -102,7 +102,7 @@ public void knownLengthBinaryDataIsPassthrough(BinaryData data, long contentLeng LocalHttpClient client = new LocalHttpClient(); HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(client).build(); - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); Response response = testInterface.testMethod(data, "application/json", contentLength); assertEquals(200, response.getStatusCode()); @@ -131,7 +131,7 @@ public void doesNotChangeBinaryDataContentType(BinaryData data, long contentLeng HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(client).build(); Class expectedContentClazz = data.getClass(); - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); Response response = testInterface.testMethod(data, ContentType.APPLICATION_JSON, contentLength); assertEquals(200, response.getStatusCode()); @@ -146,7 +146,7 @@ public void voidReturningApiClosesResponse() { LocalHttpClient client = new LocalHttpClient(); HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(client).build(); - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); testInterface.testMethodReturnsVoid(); @@ -212,7 +212,7 @@ public void doesNotChangeEncodedPath() throws IOException { return new MockHttpResponse(null, 200); }).build(); - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); testInterface.testListNext(nextLinkUri).close(); } diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java index d9c1abaede772..8c0170b8bcf52 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java @@ -10,9 +10,9 @@ import io.clientcore.core.http.models.HttpRequest; import io.clientcore.core.http.models.HttpResponse; import io.clientcore.core.http.models.Response; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.util.UriBuilder; import io.clientcore.core.util.binarydata.BinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.IOException; @@ -71,7 +71,7 @@ public interface FooService { FooSimpleResponse getFooSimpleResponse(); } - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + private static final ObjectSerializer SERIALIZER = new JsonSerializer(); private static final HttpRequest HTTP_REQUEST = new HttpRequest(HttpMethod.GET, createUri()); private static final HttpHeaderName HELLO = HttpHeaderName.fromString("hello"); private static final HttpHeaders RESPONSE_HEADERS = new HttpHeaders().set(HELLO, "world"); diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java index 698d882ba319f..af440c8e22d88 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java @@ -16,9 +16,9 @@ import io.clientcore.core.http.models.Response; import io.clientcore.core.http.pipeline.HttpPipeline; import io.clientcore.core.http.pipeline.HttpPipelineBuilder; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.util.Context; import io.clientcore.core.util.binarydata.BinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; @@ -46,7 +46,7 @@ Response testMethod(@BodyParam("application/octet-stream") BinaryData data public void voidReturningApiClosesResponse() { LocalHttpClient client = new LocalHttpClient(); HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(client).build(); - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); testInterface.testVoidMethod(Context.none()); @@ -57,7 +57,7 @@ public void voidReturningApiClosesResponse() { public void contentTypeHeaderPriorityOverBodyParamAnnotationTest() { HttpClient client = new LocalHttpClient(); HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(client).build(); - TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new DefaultJsonSerializer()); + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); byte[] bytes = "hello".getBytes(); Response response = testInterface.testMethod(BinaryData.fromStream(new ByteArrayInputStream(bytes), (long) bytes.length), diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java index 7e18022e2524a..4109f21fa958c 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java @@ -22,13 +22,13 @@ import io.clientcore.core.http.models.Response; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.implementation.http.serializer.CompositeSerializer; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.util.Base64Uri; import io.clientcore.core.implementation.util.DateTimeRfc1123; import io.clientcore.core.implementation.util.UriBuilder; import io.clientcore.core.models.SimpleClass; import io.clientcore.core.util.Context; import io.clientcore.core.util.binarydata.BinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -58,7 +58,7 @@ public class SwaggerMethodParserTests { private static final CompositeSerializer DEFAULT_SERIALIZER - = new CompositeSerializer(Arrays.asList(new DefaultJsonSerializer())); + = new CompositeSerializer(Arrays.asList(new JsonSerializer())); @ServiceInterface(name = "OperationMethods", host = "https://raw.host.com") interface OperationMethods { diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java index d6214cb09d1ff..dc3f8f733727b 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java @@ -3,7 +3,7 @@ package io.clientcore.core.implementation.serializer; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; +import io.clientcore.core.util.serializer.JsonSerializer; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -33,7 +33,7 @@ public void canSerializeAdditionalProperties() throws IOException { foo.additionalProperties().put("a.b", "c.d"); foo.additionalProperties().put("properties.bar", "barbar"); - String serialized = new String(new DefaultJsonSerializer().serializeToBytes(foo)); + String serialized = new String(new JsonSerializer().serializeToBytes(foo)); assertEquals( "{\"bar\":\"hello.world\",\"baz\":[\"hello\",\"hello.world\"],\"qux\":{\"a.b\":\"c.d\",\"bar.a\":\"ttyy\",\"bar.b\":\"uuzz\",\"hello\":\"world\"},\"additionalProperties\":{\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}}", @@ -44,7 +44,7 @@ public void canSerializeAdditionalProperties() throws IOException { public void canDeserializeAdditionalProperties() throws IOException { String wireValue = "{\"bar\":\"hello.world\",\"baz\":[\"hello\",\"hello.world\"],\"qux\":{\"a.b\":\"c.d\",\"bar.a\":\"ttyy\",\"bar.b\":\"uuzz\",\"hello\":\"world\"},\"additionalProperties\":{\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}}"; - Foo deserialized = new DefaultJsonSerializer().deserializeFromBytes(wireValue.getBytes(), Foo.class); + Foo deserialized = new JsonSerializer().deserializeFromBytes(wireValue.getBytes(), Foo.class); assertNotNull(deserialized.additionalProperties()); assertEquals("baz", deserialized.additionalProperties().get("bar")); @@ -70,7 +70,7 @@ public void canSerializeAdditionalPropertiesThroughInheritance() throws IOExcept foo.additionalProperties().put("a.b", "c.d"); foo.additionalProperties().put("properties.bar", "barbar"); - String serialized = new String(new DefaultJsonSerializer().serializeToBytes(foo)); + String serialized = new String(new JsonSerializer().serializeToBytes(foo)); assertEquals( "{\"bar\":\"hello.world\",\"baz\":[\"hello\",\"hello.world\"],\"qux\":{\"a.b\":\"c.d\",\"bar.a\":\"ttyy\",\"bar.b\":\"uuzz\",\"hello\":\"world\"},\"additionalProperties\":{\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}}", @@ -81,7 +81,7 @@ public void canSerializeAdditionalPropertiesThroughInheritance() throws IOExcept public void canDeserializeAdditionalPropertiesThroughInheritance() throws IOException { String wireValue = "{\"bar\":\"hello.world\",\"baz\":[\"hello\",\"hello.world\"],\"qux\":{\"a.b\":\"c.d\",\"bar.a\":\"ttyy\",\"bar.b\":\"uuzz\",\"hello\":\"world\"},\"additionalProperties\":{\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}}"; - FooChild deserialized = new DefaultJsonSerializer().deserializeFromBytes(wireValue.getBytes(), FooChild.class); + FooChild deserialized = new JsonSerializer().deserializeFromBytes(wireValue.getBytes(), FooChild.class); // Check additional properties are populated assertNotNull(deserialized.additionalProperties()); @@ -131,7 +131,7 @@ public void canSerializeAdditionalPropertiesWithNestedAdditionalProperties() thr nestedFoo.additionalProperties().put("name", "Sushi"); foo.additionalProperties().put("foo", nestedFoo); - String serialized = new String(new DefaultJsonSerializer().serializeToBytes(foo)); + String serialized = new String(new JsonSerializer().serializeToBytes(foo)); assertEquals( "{\"bar\":\"hello.world\",\"baz\":[\"hello\",\"hello.world\"],\"qux\":{\"a.b\":\"c.d\",\"bar.a\":\"ttyy\",\"bar.b\":\"uuzz\",\"hello\":\"world\"},\"additionalProperties\":{\"bar\":\"baz\",\"a.b\":\"c.d\",\"foo\":{\"bar\":\"bye.world\",\"additionalProperties\":{\"name\":\"Sushi\"}},\"properties.bar\":\"barbar\"}}", diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java index 7996f11254af5..c006af1a478e8 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java @@ -3,7 +3,6 @@ package io.clientcore.core.implementation.serializer; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.serialization.json.JsonReader; import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.serialization.json.JsonToken; @@ -34,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class BinaryDataSerializationTests { - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + private static final ObjectSerializer SERIALIZER = new JsonSerializer(); private static final byte[] HELLO_BYTES = "hello".getBytes(StandardCharsets.UTF_8); private static final String BASE64_HELLO_BYTES = Base64.getEncoder().encodeToString(HELLO_BYTES); private static final SimplePojo SIMPLE_POJO = new SimplePojo().setString("hello").setNumber(3.14).setBool(true); diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java index c110fe69d1554..6c649b0914d79 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java @@ -12,12 +12,12 @@ import io.clientcore.core.http.models.Response; import io.clientcore.core.implementation.http.UnexpectedExceptionInformation; import io.clientcore.core.implementation.http.serializer.CompositeSerializer; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.http.serializer.HttpResponseBodyDecoder; import io.clientcore.core.implementation.http.serializer.HttpResponseDecodeData; import io.clientcore.core.implementation.util.Base64Uri; import io.clientcore.core.implementation.util.DateTimeRfc1123; import io.clientcore.core.util.binarydata.BinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -46,7 +46,7 @@ */ public class HttpResponseBodyDecoderTests { private static final CompositeSerializer SERIALIZER - = new CompositeSerializer(Arrays.asList(new DefaultJsonSerializer())); + = new CompositeSerializer(Arrays.asList(new JsonSerializer())); private static final HttpRequest GET_REQUEST = new HttpRequest(HttpMethod.GET, "https://localhost"); private static final HttpRequest HEAD_REQUEST = new HttpRequest(HttpMethod.HEAD, "https://localhost"); @@ -100,7 +100,7 @@ private static Stream errorResponseSupplier() { @Test public void exceptionInErrorDeserializationReturnsException() { - CompositeSerializer ioExceptionThrower = new CompositeSerializer(Arrays.asList(new DefaultJsonSerializer() { + CompositeSerializer ioExceptionThrower = new CompositeSerializer(Arrays.asList(new JsonSerializer() { @Override public T deserializeFromBytes(byte[] bytes, Type type) { throw new UncheckedIOException(new IOException()); diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java index 7b76a3842c9fd..017b90702f7d5 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java @@ -3,11 +3,11 @@ package io.clientcore.core.implementation.serializer; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.serialization.json.JsonReader; import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.serialization.json.JsonToken; import io.clientcore.core.serialization.json.JsonWriter; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.Test; @@ -17,11 +17,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** - * Tests that the {@link DefaultJsonSerializer} is able to handle deserializing and serializing - * {@link JsonSerializable}. + * Tests that the {@link JsonSerializer} is able to handle deserializing and serializing {@link JsonSerializable}. */ public class JsonSerializableEndToEndTests { - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + private static final ObjectSerializer SERIALIZER = new JsonSerializer(); @Test public void serialization() throws IOException { diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java index c0f246ec374bc..1e43233ee493c 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java @@ -29,7 +29,6 @@ import io.clientcore.core.http.pipeline.HttpLoggingPolicy; import io.clientcore.core.http.pipeline.HttpPipeline; import io.clientcore.core.http.pipeline.HttpPipelineBuilder; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.util.UriBuilder; import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.Context; @@ -37,6 +36,7 @@ import io.clientcore.core.util.binarydata.ByteArrayBinaryData; import io.clientcore.core.util.binarydata.ByteBufferBinaryData; import io.clientcore.core.util.binarydata.InputStreamBinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import io.clientcore.core.util.serializer.SerializationFormat; import org.junit.jupiter.api.Assertions; @@ -325,20 +325,20 @@ public void canSendBinaryDataWithProgressReporting(BinaryData requestBody, byte[ getProtocol(ECHO_RESPONSE), new Headers(), requestBody); - + AtomicLong progress = new AtomicLong(); Context context = Contexts.empty() .setHttpRequestProgressReporter( ProgressReporter.withProgressListener(progress::set)) .getContext(); - + Response response = createHttpClient() .send(request); - + byte[] responseBytes = response .getBodyAsByteArray() .block(); - + assertArrayEquals(expectedResponseBody, responseBytes); assertEquals(expectedResponseBody.length, progress.intValue()); }*/ @@ -1362,20 +1362,20 @@ public void service20PutBodyAndHeaders() { .putBodyAndHeaders(getRequestUri(), "body string"); assertNotNull(response); assertEquals(200, response.getStatusCode()); - + assertEquals(Headers.class, response.getHeaders().getClass()); - + final HttpBinJSON body = response.getValue(); assertNotNull(body); assertMatchWithHttpOrHttps("localhost/put", body.uri()); assertEquals("body string", body.data()); - + final HttpBinHeaders headers = response.getDeserializedHeaders(); assertNotNull(headers); assertTrue(headers.accessControlAllowCredentials()); assertNotNull(headers.date()); assertNotEquals(0, (Object) headers.xProcessedTime()); - + */ } @@ -1497,7 +1497,7 @@ public void binaryDataUploadTest() throws Exception { .build(); Response response - = RestProxy.create(BinaryDataUploadService.class, httpPipeline, new DefaultJsonSerializer()) + = RestProxy.create(BinaryDataUploadService.class, httpPipeline, new JsonSerializer()) .put(getServerUri(isSecure()), data, Files.size(filePath)); assertEquals("The quick brown fox jumps over the lazy dog", response.getValue().data()); @@ -1560,13 +1560,13 @@ interface Service26 { HttpBinFormDataJSON postForm(@HostParam("uri") String uri, @FormParam("custname") String name, @FormParam("custtel") String telephone, @FormParam("custemail") String email, @FormParam("size") HttpBinFormDataJSON.PizzaSize size, @FormParam("toppings") List toppings); - + @Post("post") HttpBinFormDataJSON postEncodedForm(@HostParam("uri") String uri, @FormParam("custname") String name, @FormParam("custtel") String telephone, @FormParam(value = "custemail", encoded = true) String email, @FormParam("size") HttpBinFormDataJSON.PizzaSize size, @FormParam("toppings") List toppings); } - + @Test public void postUriForm() { Service26 service = createService(Service26.class); @@ -1578,12 +1578,12 @@ public void postUriForm() { assertEquals("123", response.form().customerTelephone()); assertEquals("foo%40bar.com", response.form().customerEmail()); assertEquals(HttpBinFormDataJSON.PizzaSize.LARGE, response.form().pizzaSize()); - + assertEquals(2, response.form().toppings().size()); assertEquals("Bacon", response.form().toppings().get(0)); assertEquals("Onion", response.form().toppings().get(1)); } - + @Test public void postUriFormEncoded() { Service26 service = createService(Service26.class); @@ -1595,7 +1595,7 @@ public void postUriFormEncoded() { assertEquals("123", response.form().customerTelephone()); assertEquals("foo@bar.com", response.form().customerEmail()); assertEquals(HttpBinFormDataJSON.PizzaSize.LARGE, response.form().pizzaSize()); - + assertEquals(2, response.form().toppings().size()); assertEquals("Bacon", response.form().toppings().get(0)); assertEquals("Onion", response.form().toppings().get(1)); @@ -2119,7 +2119,7 @@ protected T createService(Class serviceClass) { protected T createService(Class serviceClass, HttpClient httpClient) { final HttpPipeline httpPipeline = new HttpPipelineBuilder().httpClient(httpClient).build(); - return RestProxy.create(serviceClass, httpPipeline, new DefaultJsonSerializer()); + return RestProxy.create(serviceClass, httpPipeline, new JsonSerializer()); } private static void assertMatchWithHttpOrHttps(String uri1, String uri2) { diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java index 7f24e71113cf1..04c4a15abd405 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java @@ -5,8 +5,8 @@ import io.clientcore.core.http.client.HttpClient; import io.clientcore.core.http.models.ContentType; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.util.DateTimeRfc1123; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.eclipse.jetty.server.Response; import org.junit.jupiter.api.parallel.Execution; @@ -36,7 +36,7 @@ */ @Execution(ExecutionMode.SAME_THREAD) public class HttpClientTestsServer { - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + private static final ObjectSerializer SERIALIZER = new JsonSerializer(); private static final String PLAIN_RESPONSE = "/plainBytesNoHeader"; private static final String HEADER_RESPONSE = "/plainBytesWithHeader"; private static final String INVALID_HEADER_RESPONSE = "/plainBytesInvalidHeader"; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java index 2c001eab6f780..1c3142463d905 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java @@ -3,7 +3,6 @@ package io.clientcore.core.util.binarydata; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; import io.clientcore.core.implementation.util.IterableOfByteBuffersInputStream; import io.clientcore.core.models.MockFile; import io.clientcore.core.models.MockPath; @@ -12,6 +11,7 @@ import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.serialization.json.JsonToken; import io.clientcore.core.serialization.json.JsonWriter; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; @@ -52,7 +52,7 @@ * Test class for {@link BinaryData}. */ public class BinaryDataTest { - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); + private static final ObjectSerializer SERIALIZER = new JsonSerializer(); private static final byte[] RANDOM_DATA; static { @@ -525,7 +525,7 @@ public void binaryDataAsPropertySerialization() throws IOException { BinaryDataAsProperty binaryDataAsProperty = new BinaryDataAsProperty() .setProperty(BinaryData.fromObject(new BinaryDataPropertyClass().setTest("test"))); String expectedJson = "{\"property\":{\"test\":\"test\"}}"; - String actualJson = new String(new DefaultJsonSerializer().serializeToBytes(binaryDataAsProperty)); + String actualJson = new String(new JsonSerializer().serializeToBytes(binaryDataAsProperty)); assertEquals(expectedJson, actualJson); } @@ -536,7 +536,7 @@ public void binaryDataAsPropertyDeserialization() throws IOException { .setProperty(BinaryData.fromObject(new BinaryDataPropertyClass().setTest("test"))); String json = "{\"property\":{\"test\":\"test\"}}"; BinaryDataAsProperty actual - = new DefaultJsonSerializer().deserializeFromBytes(json.getBytes(), BinaryDataAsProperty.class); + = new JsonSerializer().deserializeFromBytes(json.getBytes(), BinaryDataAsProperty.class); assertEquals(expected.getProperty().toString(), actual.getProperty().toString()); } diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/DefaultJsonSerializerTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/JsonSerializerTests.java similarity index 96% rename from sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/DefaultJsonSerializerTests.java rename to sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/JsonSerializerTests.java index beb9d7f00180c..6e95a6cfa2618 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/DefaultJsonSerializerTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/JsonSerializerTests.java @@ -6,12 +6,11 @@ import io.clientcore.core.http.exception.HttpExceptionType; import io.clientcore.core.http.models.HttpMethod; import io.clientcore.core.implementation.AccessibleByteArrayOutputStream; -import io.clientcore.core.implementation.http.serializer.DefaultJsonSerializer; +import io.clientcore.core.models.SimpleClass; import io.clientcore.core.serialization.json.JsonReader; import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.serialization.json.JsonToken; import io.clientcore.core.serialization.json.JsonWriter; -import io.clientcore.core.models.SimpleClass; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -36,8 +35,8 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; -public class DefaultJsonSerializerTests { - private static final ObjectSerializer SERIALIZER = new DefaultJsonSerializer(); +public class JsonSerializerTests { + private static final ObjectSerializer SERIALIZER = new JsonSerializer(); @Test public void mapWithEmptyKeyAndNullValue() throws IOException { @@ -100,8 +99,7 @@ public static MapHolder fromJson(JsonReader jsonReader) throws IOException { @ParameterizedTest @MethodSource("deserializeJsonSupplier") public void deserializeJson(String json, DateTimeWrapper expected) throws IOException { - DateTimeWrapper actual - = new DefaultJsonSerializer().deserializeFromBytes(json.getBytes(), DateTimeWrapper.class); + DateTimeWrapper actual = new JsonSerializer().deserializeFromBytes(json.getBytes(), DateTimeWrapper.class); assertEquals(expected.getOffsetDateTime(), actual.getOffsetDateTime()); } From eb30740cd48759bb256245cd38b8084a72197ec0 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:19:52 -0500 Subject: [PATCH 5/6] Add tests --- sdk/clientcore/core/spotbugs-exclude.xml | 12 +- .../http/rest/RestProxyBase.java | 24 ---- .../http/rest/RestProxyImpl.java | 3 +- .../core/util/serializer/XmlSerializer.java | 11 +- .../clientcore/core/http/RestProxyTests.java | 6 +- .../rest/RestProxyXmlSerializableTests.java | 113 +++++++++++++++++ .../http/rest/SimpleXmlSerializable.java | 119 ++++++++++++++++++ .../BinaryDataSerializationTests.java | 1 + .../HttpResponseBodyDecoderTests.java | 3 +- .../core/shared/HttpClientTests.java | 28 ++--- 10 files changed, 267 insertions(+), 53 deletions(-) create mode 100644 sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyXmlSerializableTests.java create mode 100644 sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SimpleXmlSerializable.java diff --git a/sdk/clientcore/core/spotbugs-exclude.xml b/sdk/clientcore/core/spotbugs-exclude.xml index 9a8fc980073fc..233fc110c8971 100644 --- a/sdk/clientcore/core/spotbugs-exclude.xml +++ b/sdk/clientcore/core/spotbugs-exclude.xml @@ -29,8 +29,6 @@ - - @@ -43,6 +41,8 @@ + + @@ -124,7 +124,7 @@ - + @@ -258,12 +258,12 @@ - - + + @@ -352,7 +352,7 @@ - + diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java index 93808198ff941..4c4a3094bfd63 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyBase.java @@ -12,14 +12,12 @@ import io.clientcore.core.http.models.RequestOptions; import io.clientcore.core.http.models.Response; import io.clientcore.core.http.pipeline.HttpPipeline; -import io.clientcore.core.implementation.ReflectionSerializable; import io.clientcore.core.implementation.ReflectiveInvoker; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.implementation.http.UnexpectedExceptionInformation; import io.clientcore.core.implementation.http.serializer.CompositeSerializer; import io.clientcore.core.implementation.http.serializer.MalformedValueException; import io.clientcore.core.implementation.util.UriBuilder; -import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.binarydata.BinaryData; import io.clientcore.core.util.serializer.ObjectSerializer; @@ -29,7 +27,6 @@ import java.lang.reflect.Type; import java.net.URI; import java.net.URISyntaxException; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -253,25 +250,4 @@ public static HttpResponseException instantiateUnexpectedException( return new HttpResponseException(exceptionMessage.toString(), response, exceptionType, responseDecodedBody); } - - /** - * Whether {@code JsonSerializable} is supported and the {@code bodyContentClass} is an instance of it. - * - * @param bodyContentClass The body content class. - * @return Whether {@code bodyContentClass} can be used as {@code JsonSerializable}. - */ - static boolean supportsJsonSerializable(Class bodyContentClass) { - return ReflectionSerializable.supportsJsonSerializable(bodyContentClass); - } - - /** - * Serializes the {@code jsonSerializable} as an instance of {@code JsonSerializable}. - * - * @param jsonSerializable The {@code JsonSerializable} body content. - * @return The {@link ByteBuffer} representing the serialized {@code jsonSerializable}. - * @throws IOException If an error occurs during serialization. - */ - static ByteBuffer serializeAsJsonSerializable(JsonSerializable jsonSerializable) throws IOException { - return ReflectionSerializable.serializeJsonSerializableToByteBuffer(jsonSerializable); - } } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java index dbf0077c780a1..20647c2b5514a 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/http/rest/RestProxyImpl.java @@ -12,6 +12,7 @@ import io.clientcore.core.http.models.Response; import io.clientcore.core.http.models.ResponseBodyMode; import io.clientcore.core.http.pipeline.HttpPipeline; +import io.clientcore.core.implementation.ReflectionSerializable; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.implementation.http.HttpResponseAccessHelper; import io.clientcore.core.implementation.http.serializer.CompositeSerializer; @@ -227,7 +228,7 @@ public void updateRequest(RequestDataConfiguration requestDataConfiguration, Com } // Attempt to use JsonSerializable or XmlSerializable in a separate block. - if (supportsJsonSerializable(bodyContentObject.getClass())) { + if (ReflectionSerializable.supportsJsonSerializable(bodyContentObject.getClass())) { request.setBody(BinaryData.fromObject(bodyContentObject)); return; } diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java index 779a3d2e7ffe3..34cb4da39b769 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java @@ -75,14 +75,14 @@ public T deserializeFromStream(InputStream stream, Type type) throws IOExcep @SuppressWarnings("unchecked") private static T deserializeShared(XmlReader xmlReader, Type type) { try { - if (type instanceof Class && XmlSerializer.class.isAssignableFrom(TypeUtil.getRawClass(type))) { + if (XmlSerializable.class.isAssignableFrom(TypeUtil.getRawClass(type))) { Class clazz = (Class) type; return (T) clazz.getMethod("fromXml", XmlReader.class).invoke(null, xmlReader); } else { // TODO (alzimmer): XML needs untyped support. throw LOGGER.logThrowableAsError(new UnsupportedOperationException( - "DefaultXmlSerializer does not have support for untyped deserialization.")); + "XmlSerializer does not have support for untyped deserialization.")); } } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { throw LOGGER.logThrowableAsError(new RuntimeException(e)); @@ -91,6 +91,8 @@ private static T deserializeShared(XmlReader xmlReader, Type type) { /** * Converts the object into an XML byte array. + *

+ * This method writes both the XML declaration and contents of the object. * * @param value The object. * @return The XML binary representation of the serialized object. @@ -114,6 +116,8 @@ public byte[] serializeToBytes(Object value) throws IOException { /** * Writes an object's XML representation into a stream. + *

+ * This method writes both the XML declaration and contents of the object. * * @param stream {@link OutputStream} where the object's XML representation will be written. * @param value The object to serialize. @@ -135,11 +139,12 @@ public void serializeToStream(OutputStream stream, Object value) throws IOExcept private static void serializeShared(XmlWriter xmlWriter, Object value) { try { if (value instanceof XmlSerializable) { + xmlWriter.writeStartDocument(); ((XmlSerializable) value).toXml(xmlWriter).flush(); } else { // TODO (alzimmer): XML needs untyped support. throw LOGGER.logThrowableAsError(new UnsupportedOperationException( - "DefaultXmlSerializer does not have support for untyped serialization.")); + "XmlSerializer does not have support for untyped serialization.")); } } catch (XMLStreamException e) { throw LOGGER.logThrowableAsError(new RuntimeException(e)); diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java index 1bdbfba9f883d..a8efc67f81b8e 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java @@ -86,12 +86,12 @@ public void streamResponseShouldHaveHttpResponseReference() { HttpPipeline pipeline = new HttpPipelineBuilder() .httpClient(client) .build(); - + TestInterface testInterface = RestProxy.create(TestInterface.class, pipeline, new JsonSerializer()); StreamResponse streamResponse = testInterface.testDownload(); - + streamResponse.close(); - + // This indirectly tests that StreamResponse has HttpResponse reference. assertTrue(client.closeCalledOnResponse); }*/ diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyXmlSerializableTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyXmlSerializableTests.java new file mode 100644 index 0000000000000..113e8621084c5 --- /dev/null +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyXmlSerializableTests.java @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package io.clientcore.core.implementation.http.rest; + +import io.clientcore.core.annotation.ServiceInterface; +import io.clientcore.core.http.MockHttpResponse; +import io.clientcore.core.http.RestProxy; +import io.clientcore.core.http.annotation.BodyParam; +import io.clientcore.core.http.annotation.HttpRequestInformation; +import io.clientcore.core.http.models.HttpHeaderName; +import io.clientcore.core.http.models.HttpHeaders; +import io.clientcore.core.http.models.HttpMethod; +import io.clientcore.core.http.pipeline.HttpPipeline; +import io.clientcore.core.http.pipeline.HttpPipelineBuilder; +import io.clientcore.core.serialization.xml.XmlSerializable; +import io.clientcore.core.util.binarydata.StringBinaryData; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.function.BiConsumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Tests that {@link XmlSerializable} is supported by {@code RestProxy}. + */ +public class RestProxyXmlSerializableTests { + + @ServiceInterface(name = "XmlSerializable", host = "http://localhost") + public interface SimpleXmlSerializableProxy { + @HttpRequestInformation(method = HttpMethod.PUT, path = "sendApplicationXml", expectedStatusCodes = { 200 }) + void sendApplicationXml(@BodyParam("application/xml") SimpleXmlSerializable simpleXmlSerializable); + + @HttpRequestInformation(method = HttpMethod.PUT, path = "sendTextXml", expectedStatusCodes = { 200 }) + void sendTextXml(@BodyParam("text/xml") SimpleXmlSerializable simpleXmlSerializable); + + @HttpRequestInformation(method = HttpMethod.GET, path = "getXml", expectedStatusCodes = { 200 }) + SimpleXmlSerializable getXml(); + + @HttpRequestInformation(method = HttpMethod.GET, path = "getInvalidXml", expectedStatusCodes = { 200 }) + SimpleXmlSerializable getInvalidXml(); + } + + @Test + public void sendApplicationXml() { + sendXmlShared(SimpleXmlSerializableProxy::sendApplicationXml); + } + + @Test + public void sendTextXml() { + sendXmlShared(SimpleXmlSerializableProxy::sendTextXml); + } + + private static void sendXmlShared(BiConsumer restCall) { + SimpleXmlSerializable xmlSerializable = new SimpleXmlSerializable(true, 10, 10.D, "10"); + String singleQuoteXmlDeclaration = ""; + String doubleQuoteXmlDeclaration = ""; + String expectedBody + = "1010"; + + HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(request -> { + String body = request.getBody().toString(); + if (body.startsWith(singleQuoteXmlDeclaration)) { + assertEquals(singleQuoteXmlDeclaration + expectedBody, body); + } else { + assertEquals(doubleQuoteXmlDeclaration + expectedBody, body); + } + return new MockHttpResponse(request, 200); + }).build(); + + SimpleXmlSerializableProxy proxy = RestProxy.create(SimpleXmlSerializableProxy.class, pipeline); + restCall.accept(proxy, xmlSerializable); + } + + @ParameterizedTest + @ValueSource(strings = { "application/xml", "application/xml;charset=utf-8", "text/xml" }) + public void getXml(String contentType) { + String response = "" + + "1010"; + + HttpPipeline pipeline = new HttpPipelineBuilder() + .httpClient(request -> new MockHttpResponse(request, 200, + new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, contentType), new StringBinaryData(response))) + .build(); + + SimpleXmlSerializableProxy proxy = RestProxy.create(SimpleXmlSerializableProxy.class, pipeline); + + SimpleXmlSerializable xmlSerializable = proxy.getXml(); + + assertEquals(true, xmlSerializable.isABoolean()); + assertEquals(10, xmlSerializable.getAnInt()); + assertEquals(10.0D, xmlSerializable.getADecimal()); + assertEquals("10", xmlSerializable.getAString()); + } + + @ParameterizedTest + @ValueSource(strings = { "application/xml", "application/xml;charset=utf-8", "text/xml" }) + public void getInvalidXml(String contentType) { + String response = "" + + ""; + + HttpPipeline pipeline = new HttpPipelineBuilder() + .httpClient(request -> new MockHttpResponse(request, 200, + new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, contentType), new StringBinaryData(response))) + .build(); + + SimpleXmlSerializableProxy proxy = RestProxy.create(SimpleXmlSerializableProxy.class, pipeline); + assertThrows(RuntimeException.class, proxy::getInvalidXml); + } +} diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SimpleXmlSerializable.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SimpleXmlSerializable.java new file mode 100644 index 0000000000000..8e84dea99d8f4 --- /dev/null +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SimpleXmlSerializable.java @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package io.clientcore.core.implementation.http.rest; + +import io.clientcore.core.implementation.util.ImplUtils; +import io.clientcore.core.serialization.xml.XmlReader; +import io.clientcore.core.serialization.xml.XmlSerializable; +import io.clientcore.core.serialization.xml.XmlToken; +import io.clientcore.core.serialization.xml.XmlWriter; + +import javax.xml.stream.XMLStreamException; +import java.util.Objects; + +/** + * Test class implementing {@link XmlSerializable}. + */ +public final class SimpleXmlSerializable implements XmlSerializable { + private final boolean aBooleanAsAttribute; + private final int anInt; + private final double aDecimalAsAttribute; + private final String aString; + + public SimpleXmlSerializable(boolean aBooleanAsAttribute, int anInt, double aDecimalAsAttribute, String aString) { + this.aBooleanAsAttribute = aBooleanAsAttribute; + this.anInt = anInt; + this.aDecimalAsAttribute = aDecimalAsAttribute; + this.aString = aString; + } + + @Override + public XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException { + return toXml(xmlWriter, null); + } + + @Override + public XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException { + rootElementName = ImplUtils.isNullOrEmpty(rootElementName) ? "SimpleXml" : rootElementName; + xmlWriter.writeStartElement(rootElementName); + + xmlWriter.writeBooleanAttribute("boolean", aBooleanAsAttribute); + xmlWriter.writeDoubleAttribute("decimal", aDecimalAsAttribute); + + xmlWriter.writeIntElement("int", anInt); + xmlWriter.writeStringElement("string", aString); + + return xmlWriter.writeEndElement(); + } + + public static SimpleXmlSerializable fromXml(XmlReader xmlReader) throws XMLStreamException { + return fromXml(xmlReader, null); + } + + public static SimpleXmlSerializable fromXml(XmlReader xmlReader, String rootElementName) throws XMLStreamException { + rootElementName = ImplUtils.isNullOrEmpty(rootElementName) ? "SimpleXml" : rootElementName; + return xmlReader.readObject(rootElementName, reader -> { + boolean aBooleanAsAttribute = xmlReader.getBooleanAttribute(null, "boolean"); + double aDecimalAsAttribute = xmlReader.getDoubleAttribute(null, "decimal"); + int anInt = 0; + boolean foundAnInt = false; + String aString = null; + boolean foundAString = false; + + while (xmlReader.nextElement() != XmlToken.END_ELEMENT) { + String elementName = xmlReader.getElementName().getLocalPart(); + if ("int".equals(elementName)) { + anInt = xmlReader.getIntElement(); + foundAnInt = true; + } else if ("string".equals(elementName)) { + aString = xmlReader.getStringElement(); + foundAString = true; + } else { + xmlReader.skipElement(); + } + } + + if (foundAnInt && foundAString) { + return new SimpleXmlSerializable(aBooleanAsAttribute, anInt, aDecimalAsAttribute, aString); + } + + throw new IllegalStateException("Missing required elements."); + }); + } + + public boolean isABoolean() { + return aBooleanAsAttribute; + } + + public int getAnInt() { + return anInt; + } + + public double getADecimal() { + return aDecimalAsAttribute; + } + + public String getAString() { + return aString; + } + + @Override + public int hashCode() { + return Objects.hash(aBooleanAsAttribute, anInt, aDecimalAsAttribute, aString); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SimpleXmlSerializable)) { + return false; + } + + SimpleXmlSerializable other = (SimpleXmlSerializable) obj; + + return aBooleanAsAttribute == other.aBooleanAsAttribute + && anInt == other.anInt + && aDecimalAsAttribute == other.aDecimalAsAttribute + && Objects.equals(aString, other.aString); + } +} diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java index c006af1a478e8..255f857ef36b1 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java @@ -8,6 +8,7 @@ import io.clientcore.core.serialization.json.JsonToken; import io.clientcore.core.serialization.json.JsonWriter; import io.clientcore.core.util.binarydata.BinaryData; +import io.clientcore.core.util.serializer.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java index 6c649b0914d79..e03be28fbc1eb 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java @@ -45,8 +45,7 @@ * Tests {@link HttpResponseBodyDecoder}. */ public class HttpResponseBodyDecoderTests { - private static final CompositeSerializer SERIALIZER - = new CompositeSerializer(Arrays.asList(new JsonSerializer())); + private static final CompositeSerializer SERIALIZER = new CompositeSerializer(Arrays.asList(new JsonSerializer())); private static final HttpRequest GET_REQUEST = new HttpRequest(HttpMethod.GET, "https://localhost"); private static final HttpRequest HEAD_REQUEST = new HttpRequest(HttpMethod.HEAD, "https://localhost"); diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java index 1e43233ee493c..c554b3c6b3000 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java @@ -325,20 +325,20 @@ public void canSendBinaryDataWithProgressReporting(BinaryData requestBody, byte[ getProtocol(ECHO_RESPONSE), new Headers(), requestBody); - + AtomicLong progress = new AtomicLong(); Context context = Contexts.empty() .setHttpRequestProgressReporter( ProgressReporter.withProgressListener(progress::set)) .getContext(); - + Response response = createHttpClient() .send(request); - + byte[] responseBytes = response .getBodyAsByteArray() .block(); - + assertArrayEquals(expectedResponseBody, responseBytes); assertEquals(expectedResponseBody.length, progress.intValue()); }*/ @@ -407,7 +407,7 @@ protected URI getRequestUri(String requestPath) { } } - private static class ByteArraySerializer extends ObjectSerializer { + private static class ByteArraySerializer implements ObjectSerializer { @Override public T deserializeFromBytes(byte[] data, Type type) { return null; @@ -1362,20 +1362,20 @@ public void service20PutBodyAndHeaders() { .putBodyAndHeaders(getRequestUri(), "body string"); assertNotNull(response); assertEquals(200, response.getStatusCode()); - + assertEquals(Headers.class, response.getHeaders().getClass()); - + final HttpBinJSON body = response.getValue(); assertNotNull(body); assertMatchWithHttpOrHttps("localhost/put", body.uri()); assertEquals("body string", body.data()); - + final HttpBinHeaders headers = response.getDeserializedHeaders(); assertNotNull(headers); assertTrue(headers.accessControlAllowCredentials()); assertNotNull(headers.date()); assertNotEquals(0, (Object) headers.xProcessedTime()); - + */ } @@ -1560,13 +1560,13 @@ interface Service26 { HttpBinFormDataJSON postForm(@HostParam("uri") String uri, @FormParam("custname") String name, @FormParam("custtel") String telephone, @FormParam("custemail") String email, @FormParam("size") HttpBinFormDataJSON.PizzaSize size, @FormParam("toppings") List toppings); - + @Post("post") HttpBinFormDataJSON postEncodedForm(@HostParam("uri") String uri, @FormParam("custname") String name, @FormParam("custtel") String telephone, @FormParam(value = "custemail", encoded = true) String email, @FormParam("size") HttpBinFormDataJSON.PizzaSize size, @FormParam("toppings") List toppings); } - + @Test public void postUriForm() { Service26 service = createService(Service26.class); @@ -1578,12 +1578,12 @@ public void postUriForm() { assertEquals("123", response.form().customerTelephone()); assertEquals("foo%40bar.com", response.form().customerEmail()); assertEquals(HttpBinFormDataJSON.PizzaSize.LARGE, response.form().pizzaSize()); - + assertEquals(2, response.form().toppings().size()); assertEquals("Bacon", response.form().toppings().get(0)); assertEquals("Onion", response.form().toppings().get(1)); } - + @Test public void postUriFormEncoded() { Service26 service = createService(Service26.class); @@ -1595,7 +1595,7 @@ public void postUriFormEncoded() { assertEquals("123", response.form().customerTelephone()); assertEquals("foo@bar.com", response.form().customerEmail()); assertEquals(HttpBinFormDataJSON.PizzaSize.LARGE, response.form().pizzaSize()); - + assertEquals(2, response.form().toppings().size()); assertEquals("Bacon", response.form().toppings().get(0)); assertEquals("Onion", response.form().toppings().get(1)); From bfc4f1730a7bf41aad4a0204bddd2ed8c9c2648d Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:11:39 -0500 Subject: [PATCH 6/6] Move JsonSerializer and XmlSerializer into implementation --- sdk/clientcore/core/spotbugs-exclude.xml | 8 ++++---- .../src/main/java/io/clientcore/core/http/RestProxy.java | 4 ++-- .../util}/JsonSerializer.java | 4 +++- .../serializer => implementation/util}/XmlSerializer.java | 4 +++- .../io/clientcore/core/util/binarydata/BinaryData.java | 2 +- .../core/models/BinaryDataJavaDocCodeSnippet.java | 2 +- .../java/io/clientcore/core/http/MockHttpResponse.java | 2 +- .../test/java/io/clientcore/core/http/RestProxyTests.java | 2 +- .../rest/ResponseConstructorsCacheBenchMarkTestData.java | 2 +- .../core/implementation/http/rest/RestProxyImplTests.java | 2 +- .../http/rest/SwaggerMethodParserTests.java | 2 +- .../serializer/AdditionalPropertiesSerializerTests.java | 2 +- .../serializer/BinaryDataSerializationTests.java | 2 +- .../serializer/HttpResponseBodyDecoderTests.java | 2 +- .../serializer/JsonSerializableEndToEndTests.java | 2 +- .../java/io/clientcore/core/shared/HttpClientTests.java | 2 +- .../io/clientcore/core/shared/HttpClientTestsServer.java | 2 +- .../clientcore/core/util/binarydata/BinaryDataTest.java | 2 +- .../core/util/serializer/JsonSerializerTests.java | 1 + .../clientcore/core/util/serializer/MockSerializer.java | 2 ++ 20 files changed, 29 insertions(+), 22 deletions(-) rename sdk/clientcore/core/src/main/java/io/clientcore/core/{util/serializer => implementation/util}/JsonSerializer.java (96%) rename sdk/clientcore/core/src/main/java/io/clientcore/core/{util/serializer => implementation/util}/XmlSerializer.java (97%) diff --git a/sdk/clientcore/core/spotbugs-exclude.xml b/sdk/clientcore/core/spotbugs-exclude.xml index 233fc110c8971..211af17f94839 100644 --- a/sdk/clientcore/core/spotbugs-exclude.xml +++ b/sdk/clientcore/core/spotbugs-exclude.xml @@ -41,8 +41,8 @@ - - + + @@ -261,9 +261,9 @@ - + - + diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java index 0faa9cdf37c2e..09d16040a12f2 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/http/RestProxy.java @@ -8,9 +8,9 @@ import io.clientcore.core.implementation.http.rest.RestProxyImpl; import io.clientcore.core.implementation.http.rest.SwaggerInterfaceParser; import io.clientcore.core.implementation.http.rest.SwaggerMethodParser; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; -import io.clientcore.core.util.serializer.XmlSerializer; +import io.clientcore.core.implementation.util.XmlSerializer; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/util/JsonSerializer.java similarity index 96% rename from sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java rename to sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/util/JsonSerializer.java index 34d513b7c23f2..7c776106b7949 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/JsonSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/util/JsonSerializer.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package io.clientcore.core.util.serializer; +package io.clientcore.core.implementation.util; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.serialization.json.JsonProviders; @@ -9,6 +9,8 @@ import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.serialization.json.JsonWriter; import io.clientcore.core.util.ClientLogger; +import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.SerializationFormat; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/util/XmlSerializer.java similarity index 97% rename from sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java rename to sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/util/XmlSerializer.java index 34cb4da39b769..4580213e72a79 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/serializer/XmlSerializer.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/implementation/util/XmlSerializer.java @@ -1,13 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package io.clientcore.core.util.serializer; +package io.clientcore.core.implementation.util; import io.clientcore.core.implementation.TypeUtil; import io.clientcore.core.serialization.xml.XmlReader; import io.clientcore.core.serialization.xml.XmlSerializable; import io.clientcore.core.serialization.xml.XmlWriter; import io.clientcore.core.util.ClientLogger; +import io.clientcore.core.util.serializer.ObjectSerializer; +import io.clientcore.core.util.serializer.SerializationFormat; import javax.xml.stream.XMLStreamException; import java.io.ByteArrayOutputStream; diff --git a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java index 11770d532e926..18834318e5fd4 100644 --- a/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java +++ b/sdk/clientcore/core/src/main/java/io/clientcore/core/util/binarydata/BinaryData.java @@ -4,7 +4,7 @@ package io.clientcore.core.util.binarydata; import io.clientcore.core.serialization.json.JsonWriter; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.Closeable; diff --git a/sdk/clientcore/core/src/samples/java/io/clientcore/core/models/BinaryDataJavaDocCodeSnippet.java b/sdk/clientcore/core/src/samples/java/io/clientcore/core/models/BinaryDataJavaDocCodeSnippet.java index fbd100fd86e69..d11eb8868155d 100644 --- a/sdk/clientcore/core/src/samples/java/io/clientcore/core/models/BinaryDataJavaDocCodeSnippet.java +++ b/sdk/clientcore/core/src/samples/java/io/clientcore/core/models/BinaryDataJavaDocCodeSnippet.java @@ -5,7 +5,7 @@ import io.clientcore.core.util.ClientLogger; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.ByteArrayInputStream; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java index fac2915755a99..8aba648b66e57 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/MockHttpResponse.java @@ -7,7 +7,7 @@ import io.clientcore.core.http.models.HttpRequest; import io.clientcore.core.http.models.HttpResponse; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; public class MockHttpResponse extends HttpResponse { diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java index a8efc67f81b8e..7944835f90a04 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/http/RestProxyTests.java @@ -17,7 +17,7 @@ import io.clientcore.core.http.pipeline.HttpPipeline; import io.clientcore.core.http.pipeline.HttpPipelineBuilder; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java index 8c0170b8bcf52..07445e1ac15b8 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/ResponseConstructorsCacheBenchMarkTestData.java @@ -12,7 +12,7 @@ import io.clientcore.core.http.models.Response; import io.clientcore.core.implementation.util.UriBuilder; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import java.io.IOException; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java index af440c8e22d88..3eda4027c9476 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/RestProxyImplTests.java @@ -18,7 +18,7 @@ import io.clientcore.core.http.pipeline.HttpPipelineBuilder; import io.clientcore.core.util.Context; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java index 4109f21fa958c..ee1564058c235 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/http/rest/SwaggerMethodParserTests.java @@ -28,7 +28,7 @@ import io.clientcore.core.models.SimpleClass; import io.clientcore.core.util.Context; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java index dc3f8f733727b..a836051b31744 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/AdditionalPropertiesSerializerTests.java @@ -3,7 +3,7 @@ package io.clientcore.core.implementation.serializer; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import org.junit.jupiter.api.Test; import java.io.IOException; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java index 255f857ef36b1..4567bc58bc703 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/BinaryDataSerializationTests.java @@ -8,7 +8,7 @@ import io.clientcore.core.serialization.json.JsonToken; import io.clientcore.core.serialization.json.JsonWriter; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java index e03be28fbc1eb..7a72940183634 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/HttpResponseBodyDecoderTests.java @@ -17,7 +17,7 @@ import io.clientcore.core.implementation.util.Base64Uri; import io.clientcore.core.implementation.util.DateTimeRfc1123; import io.clientcore.core.util.binarydata.BinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java index 017b90702f7d5..873822db40134 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/implementation/serializer/JsonSerializableEndToEndTests.java @@ -7,7 +7,7 @@ import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.serialization.json.JsonToken; import io.clientcore.core.serialization.json.JsonWriter; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.Test; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java index c554b3c6b3000..c39057870b2bd 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTests.java @@ -36,7 +36,7 @@ import io.clientcore.core.util.binarydata.ByteArrayBinaryData; import io.clientcore.core.util.binarydata.ByteBufferBinaryData; import io.clientcore.core.util.binarydata.InputStreamBinaryData; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import io.clientcore.core.util.serializer.SerializationFormat; import org.junit.jupiter.api.Assertions; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java index 04c4a15abd405..1cb4e36a64d15 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/shared/HttpClientTestsServer.java @@ -6,7 +6,7 @@ import io.clientcore.core.http.client.HttpClient; import io.clientcore.core.http.models.ContentType; import io.clientcore.core.implementation.util.DateTimeRfc1123; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.eclipse.jetty.server.Response; import org.junit.jupiter.api.parallel.Execution; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java index 1c3142463d905..3dfe3ef8cbdc1 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/binarydata/BinaryDataTest.java @@ -11,7 +11,7 @@ import io.clientcore.core.serialization.json.JsonSerializable; import io.clientcore.core.serialization.json.JsonToken; import io.clientcore.core.serialization.json.JsonWriter; -import io.clientcore.core.util.serializer.JsonSerializer; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.util.serializer.ObjectSerializer; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/JsonSerializerTests.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/JsonSerializerTests.java index 6e95a6cfa2618..9371a7f081c70 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/JsonSerializerTests.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/JsonSerializerTests.java @@ -6,6 +6,7 @@ import io.clientcore.core.http.exception.HttpExceptionType; import io.clientcore.core.http.models.HttpMethod; import io.clientcore.core.implementation.AccessibleByteArrayOutputStream; +import io.clientcore.core.implementation.util.JsonSerializer; import io.clientcore.core.models.SimpleClass; import io.clientcore.core.serialization.json.JsonReader; import io.clientcore.core.serialization.json.JsonSerializable; diff --git a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java index de6f9abfbbaae..1712a6953bb09 100644 --- a/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java +++ b/sdk/clientcore/core/src/test/java/io/clientcore/core/util/serializer/MockSerializer.java @@ -3,6 +3,8 @@ package io.clientcore.core.util.serializer; +import io.clientcore.core.implementation.util.JsonSerializer; + import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Type;