diff --git a/sdk/core/Azure.Core/api/Azure.Core.net461.cs b/sdk/core/Azure.Core/api/Azure.Core.net461.cs index 3beb3c928065..2171d7bc1224 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net461.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net461.cs @@ -538,6 +538,7 @@ public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } + public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -1119,11 +1120,6 @@ public partial interface IModelSerializable T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } - public partial interface IModelXmlSerializable : Azure.Core.Serialization.IModelSerializable - { - T Deserialize(System.Xml.Linq.XElement root, Azure.Core.Serialization.ModelSerializerOptions options); - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1156,8 +1152,6 @@ public static partial class ModelSerializer { public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerFormat format) where T : Azure.Core.Serialization.IModelSerializable { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs index 93c868d71bd7..4d0b56b94516 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs @@ -538,6 +538,7 @@ public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } + public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -1119,11 +1120,6 @@ public partial interface IModelSerializable T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } - public partial interface IModelXmlSerializable : Azure.Core.Serialization.IModelSerializable - { - T Deserialize(System.Xml.Linq.XElement root, Azure.Core.Serialization.ModelSerializerOptions options); - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1156,8 +1152,6 @@ public static partial class ModelSerializer { public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerFormat format) where T : Azure.Core.Serialization.IModelSerializable { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs index 93c868d71bd7..4d0b56b94516 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs @@ -538,6 +538,7 @@ public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } + public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -1119,11 +1120,6 @@ public partial interface IModelSerializable T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } - public partial interface IModelXmlSerializable : Azure.Core.Serialization.IModelSerializable - { - T Deserialize(System.Xml.Linq.XElement root, Azure.Core.Serialization.ModelSerializerOptions options); - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1156,8 +1152,6 @@ public static partial class ModelSerializer { public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerFormat format) where T : Azure.Core.Serialization.IModelSerializable { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs index 3beb3c928065..2171d7bc1224 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs @@ -538,6 +538,7 @@ public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } + public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -1119,11 +1120,6 @@ public partial interface IModelSerializable T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } - public partial interface IModelXmlSerializable : Azure.Core.Serialization.IModelSerializable - { - T Deserialize(System.Xml.Linq.XElement root, Azure.Core.Serialization.ModelSerializerOptions options); - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1156,8 +1152,6 @@ public static partial class ModelSerializer { public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerFormat format) where T : Azure.Core.Serialization.IModelSerializable { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs index 3beb3c928065..2171d7bc1224 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs @@ -538,6 +538,7 @@ public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } + public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.IModelSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -1119,11 +1120,6 @@ public partial interface IModelSerializable T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } - public partial interface IModelXmlSerializable : Azure.Core.Serialization.IModelSerializable - { - T Deserialize(System.Xml.Linq.XElement root, Azure.Core.Serialization.ModelSerializerOptions options); - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1156,8 +1152,6 @@ public static partial class ModelSerializer { public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelJsonSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } - public static System.BinaryData ConvertToBinaryData(Azure.Core.Serialization.IModelXmlSerializable model, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } public static object Deserialize(System.BinaryData data, System.Type returnType, Azure.Core.Serialization.ModelSerializerOptions? options = null) { throw null; } public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerFormat format) where T : Azure.Core.Serialization.IModelSerializable { throw null; } diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/XmlSerializationBenchmark.cs b/sdk/core/Azure.Core/perf/SerializationBenchmark/XmlSerializationBenchmark.cs index 829fdcdfcc33..a9ff0b6f3db6 100644 --- a/sdk/core/Azure.Core/perf/SerializationBenchmark/XmlSerializationBenchmark.cs +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/XmlSerializationBenchmark.cs @@ -15,7 +15,7 @@ namespace Azure.Core.Perf { [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] - public abstract class XmlSerializationBenchmark where T : class, IModelXmlSerializable + public abstract class XmlSerializationBenchmark where T : class, IModelSerializable { private string _xml; protected T _model; @@ -95,16 +95,6 @@ public BinaryData Serialize_ModelSerializerNonGeneric() return ModelSerializer.Serialize((object)_model, _options); } - [Benchmark] - [BenchmarkCategory("PublicInterface")] - public void Serialize_PublicInterface() - { - using var stream = new MemoryStream(); - using var writer = XmlWriter.Create(stream); - _model.Serialize(writer, _options); - writer.Flush(); - } - [Benchmark] [BenchmarkCategory("Internal")] public T Deserialize_Internal() @@ -141,12 +131,5 @@ public T Deserialize_PublicInterfaceFromBinaryData() { return _model.Deserialize(_data, _options); } - - [Benchmark] - [BenchmarkCategory("PublicInterface")] - public T Deserialize_PublicInterfaceFromXElement() - { - return _model.Deserialize(_element, _options); - } } } diff --git a/sdk/core/Azure.Core/src/RequestContent.cs b/sdk/core/Azure.Core/src/RequestContent.cs index 5c174d24e716..44350bf7fb51 100644 --- a/sdk/core/Azure.Core/src/RequestContent.cs +++ b/sdk/core/Azure.Core/src/RequestContent.cs @@ -95,6 +95,14 @@ public abstract class RequestContent : IDisposable /// An instance of that wraps a serialized version of the object. public static RequestContent Create(IModelSerializable model, ModelSerializerOptions? options = default) => new ModelSerializableContent(model, options ?? ModelSerializerOptions.DefaultWireOptions); + /// + /// Creates an instance of that wraps a serialized version of an object. + /// + /// The to serialize. + /// The to use. + /// An instance of that wraps a serialized version of the object. + public static RequestContent Create(IModelJsonSerializable model, ModelSerializerOptions? options = default) => new ModelSerializableContent(model, options ?? ModelSerializerOptions.DefaultWireOptions); + /// /// Creates an instance of that wraps a serialized version of an object. /// @@ -236,11 +244,19 @@ private sealed class ModelSerializableContent : RequestContent private byte[]? _bytes; private readonly IModelSerializable _model; private readonly ModelSerializerOptions _options; + private bool _useJsonInterface; public ModelSerializableContent(IModelSerializable model, ModelSerializerOptions options) + : this(model, options, false) { } + + public ModelSerializableContent(IModelJsonSerializable model, ModelSerializerOptions options) + : this(model, options, true) { } + + private ModelSerializableContent(IModelSerializable model, ModelSerializerOptions options, bool useJsonInterface) { _model = model; _options = options; + _useJsonInterface = useJsonInterface; } public override void Dispose() => _writer?.Dispose(); @@ -279,16 +295,10 @@ private byte[] GetBytes() public override void WriteTo(Stream stream, CancellationToken cancellation) { // a model implements both xml and json we don't know the wire format and must let the model decide. - if (_model is IModelJsonSerializable jsonSerializable && _model is not IModelXmlSerializable) + if (_model is IModelJsonSerializable jsonSerializable && _useJsonInterface) { GetWriter(jsonSerializable).CopyTo(stream, cancellation); } - else if (_model is IModelXmlSerializable xmlSerializable && _model is not IModelJsonSerializable) - { - using XmlWriter writer = XmlWriter.Create(stream); - xmlSerializable.Serialize(writer, _options); - writer.Flush(); - } else { var data = GetBinaryData(); @@ -303,7 +313,7 @@ public override void WriteTo(Stream stream, CancellationToken cancellation) public override bool TryComputeLength(out long length) { - if (_model is IModelJsonSerializable jsonSerializable && _model is not IModelXmlSerializable) + if (_model is IModelJsonSerializable jsonSerializable && _useJsonInterface) return GetWriter(jsonSerializable).TryComputeLength(out length); length = 0; @@ -312,16 +322,10 @@ public override bool TryComputeLength(out long length) public override async Task WriteToAsync(Stream stream, CancellationToken cancellation) { - if (_model is IModelJsonSerializable jsonSerializable && _model is not IModelXmlSerializable) + if (_model is IModelJsonSerializable jsonSerializable && _useJsonInterface) { await GetWriter(jsonSerializable).CopyToAsync(stream, cancellation).ConfigureAwait(false); } - else if (_model is IModelXmlSerializable xmlSerializable && _model is not IModelJsonSerializable) - { - using XmlWriter writer = XmlWriter.Create(stream); - xmlSerializable.Serialize(writer, _options); - await writer.FlushAsync().ConfigureAwait(false); - } else { var data = GetBinaryData(); diff --git a/sdk/core/Azure.Core/src/Serialization/IModelXmlSerializable.cs b/sdk/core/Azure.Core/src/Serialization/IModelXmlSerializable.cs deleted file mode 100644 index 7b288f428081..000000000000 --- a/sdk/core/Azure.Core/src/Serialization/IModelXmlSerializable.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Xml; -using System.Xml.Linq; - -namespace Azure.Core.Serialization -{ - /// - /// Indicates that the implementer can be serialized and deserialized as XML. - /// - /// The type to deserialize the value into. - public interface IModelXmlSerializable : IModelSerializable - { - /// - /// Serializes the model to the provided . - /// - /// The to serialize into. - /// The to use. - void Serialize(XmlWriter writer, ModelSerializerOptions options); - - /// - /// Deserializes the XML element contained by the specified into a model. - /// - /// The that represents the model. - /// The to use. - /// A representation of the Xml element. - T Deserialize(XElement root, ModelSerializerOptions options); - } -} diff --git a/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs index e58ac3421215..28e2715b510b 100644 --- a/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs +++ b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs @@ -148,31 +148,6 @@ public static BinaryData ConvertToBinaryData(IModelJsonSerializable mode public static BinaryData ConvertToBinaryData(IModelJsonSerializable model, ModelSerializerFormat format) => ConvertToBinaryData(model, new ModelSerializerOptions(format)); - /// - /// Converts an into a . - /// - /// The model to convert. - /// The to use. - /// A binary representation of the serialized model. - public static BinaryData ConvertToBinaryData(IModelXmlSerializable model, ModelSerializerOptions? options = default) - { - options ??= ModelSerializerOptions.DefaultWireOptions; - using MemoryStream stream = new MemoryStream(); - using XmlWriter writer = XmlWriter.Create(stream); - model.Serialize(writer, options); - writer.Flush(); - return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); - } - - /// - /// Converts an into a . - /// - /// The model to convert. - /// The to use. - /// A binary representation of the serialized model. - public static BinaryData ConvertToBinaryData(IModelXmlSerializable model, ModelSerializerFormat format) - => ConvertToBinaryData(model, new ModelSerializerOptions(format)); - private static IModelSerializable GetInstance(Type returnType) { var model = GetObjectInstance(returnType) as IModelSerializable; diff --git a/sdk/core/Azure.Core/src/Shared/ModelSerializerHelper.cs b/sdk/core/Azure.Core/src/Shared/ModelSerializerHelper.cs new file mode 100644 index 000000000000..56c60feaf914 --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/ModelSerializerHelper.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using Azure.Core.Serialization; + +namespace Azure.Core +{ + internal static class ModelSerializerHelper + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ValidateFormat(IModelSerializable model, ModelSerializerFormat format) + { + bool implementsJson = model is IModelJsonSerializable; + bool isValid = (format == ModelSerializerFormat.Json && implementsJson) || format == ModelSerializerFormat.Wire; + if (!isValid) + { + throw new NotSupportedException($"The model {model.GetType().Name} does not support '{format}' format."); + } + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj b/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj index 670ff8857b03..748869f17844 100644 --- a/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj +++ b/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj @@ -23,6 +23,7 @@ + @@ -34,6 +35,9 @@ Always + + Always + Always diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelTests.cs index 6598c80a691e..a5d4f0bd2560 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelTests.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelTests.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Text.Json; using System.Xml; using Azure.Core.Serialization; using NUnit.Framework; @@ -50,74 +52,6 @@ public void RoundTripWithModelSerializerFormatOverload(string format) RoundTripTest(format, new ModelSerializerFormatOverloadStrategy()); } - [TestCase("J")] - [TestCase("W")] - public void RoundTripWithXmlInterface(string format) - { - if (ModelInstance is IModelXmlSerializable) - { - if (format == ModelSerializerFormat.Wire) - { - RoundTripTest(format, new XmlInterfaceStrategy()); - } - else - { - Assert.Throws(() => RoundTripTest(format, new XmlInterfaceStrategy())); - } - } - } - - [TestCase("J")] - [TestCase("W")] - public void RoundTripWithXmlInterfaceNonGeneric(string format) - { - if (ModelInstance is IModelXmlSerializable) - { - if (format == ModelSerializerFormat.Wire) - { - RoundTripTest(format, new XmlInterfaceNonGenericStrategy()); - } - else - { - Assert.Throws(() => RoundTripTest(format, new XmlInterfaceNonGenericStrategy())); - } - } - } - - [TestCase("J")] - [TestCase("W")] - public void RoundTripWithXmlInterfaceXElement(string format) - { - if (ModelInstance is IModelXmlSerializable) - { - if (format == ModelSerializerFormat.Wire) - { - RoundTripTest(format, new XmlInterfaceXElementStrategy()); - } - else - { - Assert.Throws(() => RoundTripTest(format, new XmlInterfaceXElementStrategy())); - } - } - } - - [TestCase("J")] - [TestCase("W")] - public void RoundTripWithXmlInterfaceXElementNonGeneric(string format) - { - if (ModelInstance is IModelXmlSerializable) - { - if (format == ModelSerializerFormat.Wire) - { - RoundTripTest(format, new XmlInterfaceXElementNonGenericStrategy()); - } - else - { - Assert.Throws(() => RoundTripTest(format, new XmlInterfaceXElementNonGenericStrategy())); - } - } - } - [TestCase("J")] [TestCase("W")] public void RoundTripWithModelInterface(string format) @@ -193,29 +127,30 @@ protected void RoundTripTest(ModelSerializerFormat format, RoundTripStrategy var expectedSerializedString = GetExpectedResult(format); - if (IsXmlWireFormat && strategy.IsExplicitJsonDeserialize && format == ModelSerializerFormat.Wire) + if (IsXmlWireFormat && (strategy.IsExplicitJsonDeserialize || strategy.IsExplicitJsonSerialize) && format == ModelSerializerFormat.Wire) { - Assert.Throws(() => { T model = strategy.Deserialize(serviceResponse, ModelInstance, options) as T; }); + if (strategy.IsExplicitJsonDeserialize) + Assert.Throws(() => { T model = strategy.Deserialize(serviceResponse, ModelInstance, options) as T; }); + if (strategy.IsExplicitJsonSerialize) + Assert.Throws(() => { var data = strategy.Serialize(ModelInstance, options); }); + } + else if (ModelInstance is not IModelJsonSerializable && format == ModelSerializerFormat.Json) + { + Assert.Throws(() => { T model = strategy.Deserialize(serviceResponse, ModelInstance, options) as T; }); + Assert.Throws(() => { var data = strategy.Serialize(ModelInstance, options); }); } else { T model = strategy.Deserialize(serviceResponse, ModelInstance, options) as T; VerifyModel(model, format); - if (IsXmlWireFormat && strategy.IsExplicitJsonSerialize && format == ModelSerializerFormat.Wire) - { - Assert.Throws(() => { var data = strategy.Serialize(model, options); }); - } - else - { - var data = strategy.Serialize(model, options); - string roundTrip = data.ToString(); + var data = strategy.Serialize(model, options); + string roundTrip = data.ToString(); - Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); - T model2 = strategy.Deserialize(roundTrip, ModelInstance, options) as T; - CompareModels(model, model2, format); - } + T model2 = strategy.Deserialize(roundTrip, ModelInstance, options) as T; + CompareModels(model, model2, format); } } @@ -224,5 +159,50 @@ protected Dictionary GetRawData(object model) var propertyInfo = model.GetType().GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); return propertyInfo.GetValue(model) as Dictionary; } + + [Test] + public void ThrowsIfUnknownFormat() + { + ModelSerializerOptions options = new ModelSerializerOptions("x"); + Assert.Throws(() => ModelSerializer.Serialize(ModelInstance, options)); + Assert.Throws(() => ModelSerializer.Deserialize(new BinaryData("x"), options)); + + Assert.Throws(() => ModelSerializer.Serialize((IModelSerializable)ModelInstance, options)); + Assert.Throws(() => ModelSerializer.Deserialize(new BinaryData("x"), typeof(T), options)); + if (ModelInstance is IModelJsonSerializable jsonModel) + { + Assert.Throws(() => jsonModel.Serialize(new Utf8JsonWriter(new MemoryStream()), options)); + Assert.Throws(() => ((IModelJsonSerializable)jsonModel).Serialize(new Utf8JsonWriter(new MemoryStream()), options)); + bool gotException = false; + try + { + Utf8JsonReader reader = default; + jsonModel.Deserialize(ref reader, options); + } + catch (NotSupportedException) + { + gotException = true; + } + finally + { + Assert.IsTrue(gotException); + } + + gotException = false; + try + { + Utf8JsonReader reader = default; + ((IModelJsonSerializable)jsonModel).Deserialize(ref reader, options); + } + catch (NotSupportedException) + { + gotException = true; + } + finally + { + Assert.IsTrue(gotException); + } + } + } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlCrossLibraryTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlCrossLibraryTests.cs new file mode 100644 index 000000000000..37c42607c3ef --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlCrossLibraryTests.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using NUnit.Framework; +using Azure.Core.Tests.Public.ModelSerializationTests.Models; +using Azure.Core.Serialization; +using System; +using System.Text.Json; +using System.Xml; +using System.Reflection; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class ModelXmlCrossLibraryTests : ModelTests + { + protected override string WirePayload => File.ReadAllText(Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName, "ModelSerializationTests", "TestData", "ModelXmlX.xml")).TrimEnd(); + + protected override string JsonPayload => "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"childTag\":{\"childValue\":\"ChildRed\",\"childReadOnlyProperty\":\"ChildReadOnly\"}}"; + + protected override Func ToRequestContent => model => model; + + protected override Func FromResponse => response => (ModelXmlCrossLibrary)response; + + [Test] + public void ThrowsIfMismatch() + { + ModelSerializerOptions jsonOptions = new ModelSerializerOptions(ModelSerializerFormat.Json); + ModelXmlCrossLibrary model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(JsonPayload)), jsonOptions); + + Assert.Throws(Is.InstanceOf(), () => ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(WirePayload)), jsonOptions)); + + ModelSerializerOptions wireOptions = ModelSerializerOptions.DefaultWireOptions; + Assert.Throws(() => ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(JsonPayload)), wireOptions)); + } + + protected override string GetExpectedResult(ModelSerializerFormat format) + { + if (format == ModelSerializerFormat.Wire) + { + var expectedSerializedString = "\uFEFFColorRed"; + if (format.Equals(ModelSerializerFormat.Json)) + expectedSerializedString += "ReadOnly"; + expectedSerializedString += "ChildRed"; + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Json)) + // expectedSerializedString += "ChildReadOnly"; + expectedSerializedString += ""; + return expectedSerializedString; + } + if (format == ModelSerializerFormat.Json) + { + var expectedSerializedString = "{\"key\":\"Color\",\"value\":\"Red\""; + if (format.Equals(ModelSerializerFormat.Json)) + expectedSerializedString += ",\"readOnlyProperty\":\"ReadOnly\""; + expectedSerializedString += ",\"childTag\":{\"childValue\":\"ChildRed\""; + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Json)) + // expectedSerializedString += ",\"childReadOnlyProperty\":\"ChildReadOnly\""; + expectedSerializedString += "}}"; + return expectedSerializedString; + } + throw new InvalidOperationException($"Unknown format used in test {format}"); + } + + protected override void VerifyModel(ModelXmlCrossLibrary model, ModelSerializerFormat format) + { + Assert.AreEqual("Color", model.Key); + Assert.AreEqual("Red", model.Value); + Assert.AreEqual("ReadOnly", model.ReadOnlyProperty); + Assert.IsNotNull(model.ChildModelXml); + Assert.AreEqual("ChildRed", model.ChildModelXml.ChildValue); + Assert.AreEqual("ChildReadOnly", model.ChildModelXml.ChildReadOnlyProperty); + } + + protected override void CompareModels(ModelXmlCrossLibrary model, ModelXmlCrossLibrary model2, ModelSerializerFormat format) + { + Assert.AreEqual(model.Key, model2.Key); + Assert.AreEqual(model.Value, model2.Value); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual(model.ReadOnlyProperty, model2.ReadOnlyProperty); + Assert.AreEqual(model.ChildModelXml.ChildValue, model2.ChildModelXml.ChildValue); + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Data)) + // Assert.AreEqual(model.RenamedChildModelXml.ChildReadOnlyProperty, model2.RenamedChildModelXml.ChildReadOnlyProperty); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlOnlyTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlOnlyTests.cs new file mode 100644 index 000000000000..98a06f8a0a43 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlOnlyTests.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using NUnit.Framework; +using Azure.Core.Tests.Public.ModelSerializationTests.Models; +using Azure.Core.Serialization; +using System; +using System.Text.Json; +using System.Xml; +using System.Reflection; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class ModelXmlOnlyTests : ModelTests + { + protected override string WirePayload => File.ReadAllText(Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName, "ModelSerializationTests", "TestData", "ModelXml.xml")).TrimEnd(); + + protected override string JsonPayload => string.Empty; + + protected override Func ToRequestContent => model => model; + + protected override Func FromResponse => response => (ModelXmlOnly)response; + + protected override string GetExpectedResult(ModelSerializerFormat format) + { + var expectedSerializedString = "\uFEFFColorRed"; + if (format.Equals(ModelSerializerFormat.Json)) + expectedSerializedString += "ReadOnly"; + expectedSerializedString += "ChildRed"; + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Json)) + // expectedSerializedString += "ChildReadOnly"; + expectedSerializedString += ""; + return expectedSerializedString; + } + + protected override void VerifyModel(ModelXmlOnly model, ModelSerializerFormat format) + { + Assert.AreEqual("Color", model.Key); + Assert.AreEqual("Red", model.Value); + Assert.AreEqual("ReadOnly", model.ReadOnlyProperty); + Assert.IsNotNull(model.RenamedChildModelXml); + Assert.AreEqual("ChildRed", model.RenamedChildModelXml.ChildValue); + Assert.AreEqual("ChildReadOnly", model.RenamedChildModelXml.ChildReadOnlyProperty); + } + + protected override void CompareModels(ModelXmlOnly model, ModelXmlOnly model2, ModelSerializerFormat format) + { + Assert.AreEqual(model.Key, model2.Key); + Assert.AreEqual(model.Value, model2.Value); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual(model.ReadOnlyProperty, model2.ReadOnlyProperty); + Assert.AreEqual(model.RenamedChildModelXml.ChildValue, model2.RenamedChildModelXml.ChildValue); + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Json)) + // Assert.AreEqual(model.RenamedChildModelXml.ChildReadOnlyProperty, model2.RenamedChildModelXml.ChildReadOnlyProperty); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs index c518749f4052..a53ce7110c7c 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs @@ -23,14 +23,6 @@ internal class ModelXmlTests : ModelTests protected override Func FromResponse => response => (ModelXml)response; - [Test] - public void ThrowsIfUnknownFormat() - { - ModelSerializerOptions options = new ModelSerializerOptions("x"); - Assert.Throws(() => ModelSerializer.Serialize(new ModelXml(), options)); - Assert.Throws(() => ModelSerializer.Deserialize(new BinaryData("x"), options)); - } - [Test] public void ThrowsIfMismatch() { @@ -90,7 +82,7 @@ protected override void CompareModels(ModelXml model, ModelXml model2, ModelSeri Assert.AreEqual(model.ReadOnlyProperty, model2.ReadOnlyProperty); Assert.AreEqual(model.RenamedChildModelXml.ChildValue, model2.RenamedChildModelXml.ChildValue); //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions - //if (format.Equals(ModelSerializerFormat.Data)) + //if (format.Equals(ModelSerializerFormat.Json)) // Assert.AreEqual(model.RenamedChildModelXml.ChildReadOnlyProperty, model2.RenamedChildModelXml.ChildReadOnlyProperty); } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXml.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXml.cs index f8526daaf11d..06ad07441a77 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXml.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXml.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.Json; using System.Xml; using System.Xml.Linq; @@ -12,7 +13,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { [XmlRoot("ChildTag")] - public class ChildModelXml : IXmlSerializable, IModelXmlSerializable, IModelJsonSerializable, IUtf8JsonSerializable + public class ChildModelXml : IXmlSerializable, IModelSerializable, IModelJsonSerializable, IUtf8JsonSerializable { internal ChildModelXml() { } @@ -37,14 +38,6 @@ public ChildModelXml(string value, string readonlyProperty) void IXmlSerializable.Write(XmlWriter writer, string nameHint) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); - void IModelXmlSerializable.Serialize(XmlWriter writer, ModelSerializerOptions options) - { - if (options.Format != ModelSerializerFormat.Wire) - throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Wire}' format when calling the {nameof(IModelXmlSerializable)} interface"); - - Serialize(writer, options, null); - } - private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) { writer.WriteStartElement(nameHint ?? "ChildTag"); @@ -103,29 +96,36 @@ internal static ChildModelXml DeserializeChildModelXml(JsonElement element, Mode ChildModelXml IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format == ModelSerializerFormat.Json) { using var doc = JsonDocument.Parse(data); return DeserializeChildModelXml(doc.RootElement, options); } - if (options.Format == ModelSerializerFormat.Wire) + else { return DeserializeChildModelXml(XElement.Load(data.ToStream()), options); } - throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); } BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format == ModelSerializerFormat.Json) { - return ModelSerializer.ConvertToBinaryData((IModelJsonSerializable)this, options); + return ModelSerializer.ConvertToBinaryData(this, options); } - if (options.Format == ModelSerializerFormat.Wire) + else { - return ModelSerializer.ConvertToBinaryData((IModelXmlSerializable)this, options); + options ??= ModelSerializerOptions.DefaultWireOptions; + using MemoryStream stream = new MemoryStream(); + using XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options, null); + writer.Flush(); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); } - throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); } private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) @@ -141,18 +141,22 @@ private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) writer.WriteEndObject(); } - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => + void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + Serialize(writer, options); + } ChildModelXml IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + using var doc = JsonDocument.ParseValue(ref reader); return DeserializeChildModelXml(doc.RootElement, options); } void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions); - - ChildModelXml IModelXmlSerializable.Deserialize(XElement root, ModelSerializerOptions options) => DeserializeChildModelXml(root, options); } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXmlOnly.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXmlOnly.cs new file mode 100644 index 000000000000..2cb48a59ba9f --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXmlOnly.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Serialization; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + [XmlRoot("ChildTag")] + public class ChildModelXmlOnly : IXmlSerializable, IModelSerializable + { + internal ChildModelXmlOnly() { } + + /// Initializes a new instance of ModelXml for testing. + /// + /// is null. + public ChildModelXmlOnly(string value, string readonlyProperty) + { + Argument.AssertNotNull(value, nameof(value)); + + ChildValue = value; + ChildReadOnlyProperty = readonlyProperty; + } + + /// Gets or sets the value. + [XmlElement("ChildValue")] + public string ChildValue { get; set; } + /// Gets or sets the value. + [XmlElement("ChildReadOnlyProperty")] + public string ChildReadOnlyProperty { get; } + + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => + Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); + + private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) + { + if (options.Format != ModelSerializerFormat.Wire) + throw new NotSupportedException($"{nameof(ChildModelXmlOnly)} does not support '{options.Format}' format"); + + writer.WriteStartElement(nameHint ?? "ChildTag"); + writer.WriteStartElement("ChildValue"); + writer.WriteValue(ChildValue); + writer.WriteEndElement(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WriteStartElement("ChildReadOnlyProperty"); + writer.WriteValue(ChildReadOnlyProperty); + writer.WriteEndElement(); + } + writer.WriteEndElement(); + } + + internal static ChildModelXmlOnly DeserializeChildModelXmlOnly(XElement element, ModelSerializerOptions options = default) + { + options ??= ModelSerializerOptions.DefaultWireOptions; + + if (options.Format != ModelSerializerFormat.Wire) + throw new NotSupportedException($"{nameof(ChildModelXmlOnly)} does not support '{options.Format}' format"); + + string value = default; + string readonlyProperty = default; + if (element.Element("ChildValue") is XElement valueElement) + { + value = (string)valueElement; + } + if (element.Element("ChildReadOnlyProperty") is XElement readonlyPropertyElement) + { + readonlyProperty = (string)readonlyPropertyElement; + } + return new ChildModelXmlOnly(value, readonlyProperty); + } + + ChildModelXmlOnly IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + => DeserializeChildModelXmlOnly(XElement.Load(data.ToStream()), options); + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + options ??= ModelSerializerOptions.DefaultWireOptions; + using MemoryStream stream = new MemoryStream(); + using XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options, null); + writer.Flush(); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/BaseModel.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/BaseModel.cs index 1af30c9a31c1..ae7a3b127dc9 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/BaseModel.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/BaseModel.cs @@ -28,7 +28,12 @@ public static explicit operator BaseModel(Response response) void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IModelJsonSerializable)this).Serialize(writer, ModelSerializerOptions.DefaultWireOptions); - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + Serialize(writer, options); + } private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { @@ -104,14 +109,25 @@ internal static BaseModel DeserializeBaseModel(JsonElement element, ModelSeriali } BaseModel IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) - => DeserializeBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return DeserializeBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); + } BaseModel IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + using var doc = JsonDocument.ParseValue(ref reader); return DeserializeBaseModel(doc.RootElement, options); } - BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializer.ConvertToBinaryData(this, options); + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return ModelSerializer.ConvertToBinaryData(this, options); + } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelX.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelX.cs index 974e10027dac..7c38293509e1 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelX.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelX.cs @@ -40,7 +40,12 @@ public static explicit operator ModelX(Response response) return DeserializeModelX(jsonDocument.RootElement, ModelSerializerOptions.DefaultWireOptions); } - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + Serialize(writer, options); + } private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { @@ -113,6 +118,8 @@ internal static ModelX DeserializeModelX(JsonElement element, ModelSerializerOpt ModelX IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + return DeserializeModelX(JsonDocument.Parse(data.ToString()).RootElement, options); } @@ -124,10 +131,17 @@ public void Serialize(Utf8JsonWriter writer) ModelX IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + using var doc = JsonDocument.ParseValue(ref reader); return DeserializeModelX(doc.RootElement, options); } - BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializer.ConvertToBinaryData(this, options); + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return ModelSerializer.ConvertToBinaryData(this, options); + } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/UnknownBaseModel.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/UnknownBaseModel.cs index b6b8a78f3a46..f2dc1d0311cb 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/UnknownBaseModel.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/UnknownBaseModel.cs @@ -26,7 +26,12 @@ internal UnknownBaseModel(string kind, string name, Dictionary Serialize(writer, ModelSerializerOptions.DefaultWireOptions); - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + Serialize(writer, options); + } private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { @@ -58,15 +63,24 @@ private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) BaseModel IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + return DeserializeUnknownBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); } BaseModel IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + using var doc = JsonDocument.ParseValue(ref reader); return DeserializeUnknownBaseModel(doc.RootElement, options); } - BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializer.ConvertToBinaryData(this, options); + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return ModelSerializer.ConvertToBinaryData(this, options); + } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Envelope.cs similarity index 92% rename from sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs rename to sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Envelope.cs index e27274608985..0c698821f14f 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Envelope.cs @@ -49,7 +49,12 @@ public static explicit operator Envelope(Response response) #region Serialization void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IModelJsonSerializable>)this).Serialize(writer, ModelSerializerOptions.DefaultWireOptions); - void IModelJsonSerializable>.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + void IModelJsonSerializable>.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + Serialize(writer, options); + } private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { @@ -147,16 +152,25 @@ private static T DeserializeT(JsonElement element, ModelSerializerOptions option Envelope IModelSerializable>.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + return DeserializeEnvelope(JsonDocument.Parse(data.ToString()).RootElement, options); } Envelope IModelJsonSerializable>.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + using var doc = JsonDocument.ParseValue(ref reader); return DeserializeEnvelope(doc.RootElement, options); } - BinaryData IModelSerializable>.Serialize(ModelSerializerOptions options) => ModelSerializer.ConvertToBinaryData(this, options); + BinaryData IModelSerializable>.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return ModelSerializer.ConvertToBinaryData(this, options); + } #endregion } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs index a67ce49df4bd..56e28ce01f84 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.Json; using System.Xml; using System.Xml.Linq; @@ -12,7 +13,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { [XmlRoot("Tag")] - public class ModelXml : IXmlSerializable, IModelXmlSerializable, IModelJsonSerializable + public class ModelXml : IXmlSerializable, IModelSerializable, IModelJsonSerializable, IUtf8JsonSerializable { internal ModelXml() { } @@ -45,7 +46,7 @@ public ModelXml(string key, string value, string readonlyProperty, ChildModelXml public static implicit operator RequestContent(ModelXml modelXml) { - return RequestContent.Create(modelXml, ModelSerializerOptions.DefaultWireOptions); + return RequestContent.Create((IModelSerializable)modelXml, ModelSerializerOptions.DefaultWireOptions); } public static explicit operator ModelXml(Response response) @@ -57,14 +58,6 @@ public static explicit operator ModelXml(Response response) void IXmlSerializable.Write(XmlWriter writer, string nameHint) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); - void IModelXmlSerializable.Serialize(XmlWriter writer, ModelSerializerOptions options) - { - if (options.Format != ModelSerializerFormat.Wire) - throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Wire}' format when calling the {nameof(IModelXmlSerializable)} interface"); - - Serialize(writer, options, null); - } - private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) { writer.WriteStartElement(nameHint ?? "Tag"); @@ -130,15 +123,21 @@ public static ModelXml DeserializeModelXml(XElement element, ModelSerializerOpti BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format == ModelSerializerFormat.Json) { - return ModelSerializer.ConvertToBinaryData((IModelJsonSerializable)this, options); + return ModelSerializer.ConvertToBinaryData(this, options); } - if (options.Format == ModelSerializerFormat.Wire) + else { - return ModelSerializer.ConvertToBinaryData((IModelXmlSerializable)this, options); + options ??= ModelSerializerOptions.DefaultWireOptions; + using MemoryStream stream = new MemoryStream(); + using XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options, null); + writer.Flush(); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); } - throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); } internal static ModelXml DeserializeModelXml(JsonElement element, ModelSerializerOptions options = default) @@ -179,22 +178,23 @@ internal static ModelXml DeserializeModelXml(JsonElement element, ModelSerialize ModelXml IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format == ModelSerializerFormat.Json) { using var doc = JsonDocument.Parse(data); return DeserializeModelXml(doc.RootElement, options); } - if (options.Format == ModelSerializerFormat.Wire) + else { return DeserializeModelXml(XElement.Load(data.ToStream()), options); } - throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); } - ModelXml IModelXmlSerializable.Deserialize(XElement root, ModelSerializerOptions options) => DeserializeModelXml(root, options); - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format != ModelSerializerFormat.Json) throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Json}' format when calling the {nameof(IModelJsonSerializable)} interface"); Serialize(writer, options); @@ -202,10 +202,14 @@ void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSeri ModelXml IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format != ModelSerializerFormat.Json) throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Json}' format when calling the {nameof(IModelJsonSerializable)} interface"); using var doc = JsonDocument.ParseValue(ref reader); return DeserializeModelXml(doc.RootElement, options); } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions); } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXmlCrossLibrary.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXmlCrossLibrary.cs new file mode 100644 index 000000000000..a978e4230ce5 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXmlCrossLibrary.cs @@ -0,0 +1,222 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Serialization; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + [XmlRoot("Tag")] + public class ModelXmlCrossLibrary : IXmlSerializable, IModelSerializable, IModelJsonSerializable, IUtf8JsonSerializable + { + internal ModelXmlCrossLibrary() { } + + /// Initializes a new instance of ModelXml for testing. + /// + /// + /// or is null. + public ModelXmlCrossLibrary(string key, string value, string readonlyProperty, ChildModelXml childModelXml) + { + Argument.AssertNotNull(key, nameof(key)); + Argument.AssertNotNull(value, nameof(value)); + + Key = key; + Value = value; + ReadOnlyProperty = readonlyProperty; + ChildModelXml = childModelXml; + } + + /// Gets or sets the key. + [XmlElement("Key")] + public string Key { get; set; } + /// Gets or sets the value. + [XmlElement("Value")] + public string Value { get; set; } + /// Gets or sets the value. + [XmlElement("ReadOnlyProperty")] + public string ReadOnlyProperty { get; } + [XmlElement("ChildTag")] + public ChildModelXml ChildModelXml { get; set; } + + public static implicit operator RequestContent(ModelXmlCrossLibrary modelXmlCrossLibrary) + { + return RequestContent.Create((IModelSerializable)modelXmlCrossLibrary, ModelSerializerOptions.DefaultWireOptions); + } + + public static explicit operator ModelXmlCrossLibrary(Response response) + { + return DeserializeModelXmlCrossLibrary(XElement.Load(response.ContentStream), ModelSerializerOptions.DefaultWireOptions); + } + + public void Serialize(XmlWriter writer, string nameHint) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); + + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); + + private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) + { + writer.WriteStartElement(nameHint ?? "Tag"); + writer.WriteStartElement("Key"); + writer.WriteValue(Key); + writer.WriteEndElement(); + writer.WriteStartElement("Value"); + writer.WriteValue(Value); + writer.WriteEndElement(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WriteStartElement("ReadOnlyProperty"); + writer.WriteValue(ReadOnlyProperty); + writer.WriteEndElement(); + } + var childModelXml = ModelSerializer.Serialize(ChildModelXml, options); + var bytes = childModelXml.ToArray(); + int start = bytes.AsSpan(1).IndexOf((byte)'>') + 2; + var chars = Encoding.UTF8.GetChars(bytes, start, bytes.Length - start); + writer.WriteRaw(chars, 0, chars.Length); + writer.WriteEndElement(); + } + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("key"u8); + writer.WriteStringValue(Key); + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("readOnlyProperty"u8); + writer.WriteStringValue(ReadOnlyProperty); + } + writer.WritePropertyName("childTag"u8); + writer.WriteObjectValue(ChildModelXml); + writer.WriteEndObject(); + } + + public static ModelXmlCrossLibrary DeserializeModelXmlCrossLibrary(XElement element, ModelSerializerOptions options = default) + { + options ??= ModelSerializerOptions.DefaultWireOptions; + + string key = default; + string value = default; + string readonlyProperty = default; + ChildModelXml childModelXml = default; + if (element.Element("Key") is XElement keyElement) + { + key = (string)keyElement; + } + if (element.Element("Value") is XElement valueElement) + { + value = (string)valueElement; + } + if (element.Element("ReadOnlyProperty") is XElement readonlyPropertyElement) + { + readonlyProperty = (string)readonlyPropertyElement; + } + if (element.Element("ChildTag") is XElement renamedChildModelXmlElement) + { + using MemoryStream stream = new MemoryStream(); + renamedChildModelXmlElement.Save(stream); + childModelXml = ModelSerializer.Deserialize(new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)), options); + } + return new ModelXmlCrossLibrary(key, value, readonlyProperty, childModelXml); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + if (options.Format == ModelSerializerFormat.Json) + { + return ModelSerializer.ConvertToBinaryData(this, options); + } + else + { + options ??= ModelSerializerOptions.DefaultWireOptions; + using MemoryStream stream = new MemoryStream(); + using XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options, null); + writer.Flush(); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); + } + } + + internal static ModelXmlCrossLibrary DeserializeModelXmlCrossLibrary(JsonElement element, ModelSerializerOptions options = default) + { + options ??= ModelSerializerOptions.DefaultWireOptions; + + string key = default; + string value = default; + string readOnlyProperty = default; + ChildModelXml childModelXml = default; + + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("key"u8)) + { + key = property.Value.GetString(); + continue; + } + if (property.NameEquals("value"u8)) + { + value = property.Value.GetString(); + continue; + } + if (property.NameEquals("readOnlyProperty"u8)) + { + readOnlyProperty = property.Value.GetString(); + continue; + } + if (property.NameEquals("childTag"u8)) + { + childModelXml = ChildModelXml.DeserializeChildModelXml(property.Value, options); + continue; + } + } + return new ModelXmlCrossLibrary(key, value, readOnlyProperty, childModelXml); + } + + ModelXmlCrossLibrary IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + if (options.Format == ModelSerializerFormat.Json) + { + using var doc = JsonDocument.Parse(data); + return DeserializeModelXmlCrossLibrary(doc.RootElement, options); + } + else + { + return DeserializeModelXmlCrossLibrary(XElement.Load(data.ToStream()), options); + } + } + + void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + if (options.Format != ModelSerializerFormat.Json) + throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Json}' format when calling the {nameof(IModelJsonSerializable)} interface"); + Serialize(writer, options); + } + + ModelXmlCrossLibrary IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + if (options.Format != ModelSerializerFormat.Json) + throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Json}' format when calling the {nameof(IModelJsonSerializable)} interface"); + using var doc = JsonDocument.ParseValue(ref reader); + return DeserializeModelXmlCrossLibrary(doc.RootElement, options); + } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions); + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXmlOnly.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXmlOnly.cs new file mode 100644 index 000000000000..6c986bd721a6 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXmlOnly.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Serialization; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + [XmlRoot("Tag")] + public class ModelXmlOnly : IXmlSerializable, IModelSerializable + { + internal ModelXmlOnly() { } + + /// Initializes a new instance of ModelXml for testing. + /// + /// + /// or is null. + public ModelXmlOnly(string key, string value, string readonlyProperty, ChildModelXmlOnly childModel) + { + Argument.AssertNotNull(key, nameof(key)); + Argument.AssertNotNull(value, nameof(value)); + + Key = key; + Value = value; + ReadOnlyProperty = readonlyProperty; + RenamedChildModelXml = childModel; + } + + /// Gets or sets the key. + [XmlElement("Key")] + public string Key { get; set; } + /// Gets or sets the value. + [XmlElement("Value")] + public string Value { get; set; } + /// Gets or sets the value. + [XmlElement("ReadOnlyProperty")] + public string ReadOnlyProperty { get; } + [XmlElement("RenamedChildModelXml")] + public ChildModelXmlOnly RenamedChildModelXml { get; set; } + + public static implicit operator RequestContent(ModelXmlOnly modelXml) + { + return RequestContent.Create(modelXml, ModelSerializerOptions.DefaultWireOptions); + } + + public static explicit operator ModelXmlOnly(Response response) + { + return DeserializeModelXmlOnly(XElement.Load(response.ContentStream), ModelSerializerOptions.DefaultWireOptions); + } + + public void Serialize(XmlWriter writer, string nameHint) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); + + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); + + private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) + { + writer.WriteStartElement(nameHint ?? "Tag"); + writer.WriteStartElement("Key"); + writer.WriteValue(Key); + writer.WriteEndElement(); + writer.WriteStartElement("Value"); + writer.WriteValue(Value); + writer.WriteEndElement(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WriteStartElement("ReadOnlyProperty"); + writer.WriteValue(ReadOnlyProperty); + writer.WriteEndElement(); + } + writer.WriteObjectValue(RenamedChildModelXml, "RenamedChildModelXml"); + writer.WriteEndElement(); + } + + public static ModelXmlOnly DeserializeModelXmlOnly(XElement element, ModelSerializerOptions options = default) + { + options ??= ModelSerializerOptions.DefaultWireOptions; + + string key = default; + string value = default; + string readonlyProperty = default; + ChildModelXmlOnly childModelXml = default; + if (element.Element("Key") is XElement keyElement) + { + key = (string)keyElement; + } + if (element.Element("Value") is XElement valueElement) + { + value = (string)valueElement; + } + if (element.Element("ReadOnlyProperty") is XElement readonlyPropertyElement) + { + readonlyProperty = (string)readonlyPropertyElement; + } + if (element.Element("RenamedChildModelXml") is XElement renamedChildModelXmlElement) + { + childModelXml = ChildModelXmlOnly.DeserializeChildModelXmlOnly(renamedChildModelXmlElement, options); + } + return new ModelXmlOnly(key, value, readonlyProperty, childModelXml); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + using MemoryStream stream = new MemoryStream(); + using XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options, null); + writer.Flush(); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); + } + + ModelXmlOnly IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return DeserializeModelXmlOnly(XElement.Load(data.ToStream()), options); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs index 4a8dc6c1be98..6fd7e8ee1c18 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.Json; using System.Xml; using System.Xml.Linq; @@ -12,7 +13,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { [XmlRoot("Tag")] - internal class XmlModelForCombinedInterface : IXmlSerializable, IModelXmlSerializable, IModelJsonSerializable + internal class XmlModelForCombinedInterface : IXmlSerializable, IModelSerializable, IModelJsonSerializable, IUtf8JsonSerializable { public XmlModelForCombinedInterface() { } @@ -42,7 +43,7 @@ public XmlModelForCombinedInterface(string key, string value, string readOnlyPro public static implicit operator RequestContent(XmlModelForCombinedInterface xmlModelForCombinedInterface) { - return RequestContent.Create(xmlModelForCombinedInterface, ModelSerializerOptions.DefaultWireOptions); + return RequestContent.Create((IModelSerializable)xmlModelForCombinedInterface, ModelSerializerOptions.DefaultWireOptions); } public static explicit operator XmlModelForCombinedInterface(Response response) @@ -53,14 +54,6 @@ public static explicit operator XmlModelForCombinedInterface(Response response) void IXmlSerializable.Write(XmlWriter writer, string nameHint) => Serialize(writer, ModelSerializerOptions.DefaultWireOptions, nameHint); - void IModelXmlSerializable.Serialize(XmlWriter writer, ModelSerializerOptions options) - { - if (options.Format != ModelSerializerFormat.Wire) - throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Wire}' format when calling the {nameof(IModelXmlSerializable)} interface"); - - Serialize(writer, options, null); - } - internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInterface(XElement element, ModelSerializerOptions options = default) { options ??= ModelSerializerOptions.DefaultWireOptions; @@ -118,15 +111,21 @@ private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format == ModelSerializerFormat.Json) { - return ModelSerializer.ConvertToBinaryData((IModelJsonSerializable)this, options); + return ModelSerializer.ConvertToBinaryData(this, options); } - if (options.Format == ModelSerializerFormat.Wire) + else { - return ModelSerializer.ConvertToBinaryData((IModelXmlSerializable)this, options); + options ??= ModelSerializerOptions.DefaultWireOptions; + using MemoryStream stream = new MemoryStream(); + using XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options, null); + writer.Flush(); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); } - throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); } internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInterface(JsonElement element, ModelSerializerOptions options = default) @@ -161,22 +160,23 @@ internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInter XmlModelForCombinedInterface IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format == ModelSerializerFormat.Json) { using var doc = JsonDocument.Parse(data); return DeserializeXmlModelForCombinedInterface(doc.RootElement, options); } - if (options.Format == ModelSerializerFormat.Wire) + else { return DeserializeXmlModelForCombinedInterface(XElement.Load(data.ToStream()), options); } - throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); } - XmlModelForCombinedInterface IModelXmlSerializable.Deserialize(XElement root, ModelSerializerOptions options) => DeserializeXmlModelForCombinedInterface(root, options); - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format != ModelSerializerFormat.Json) throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Json}' format when calling the {nameof(IModelJsonSerializable)} interface"); @@ -185,11 +185,16 @@ void IModelJsonSerializable.Serialize(Utf8JsonWrit XmlModelForCombinedInterface IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (options.Format != ModelSerializerFormat.Json) throw new InvalidOperationException($"Must use '{ModelSerializerFormat.Json}' format when calling the {nameof(IModelJsonSerializable)} interface"); using var doc = JsonDocument.ParseValue(ref reader); return DeserializeXmlModelForCombinedInterface(doc.RootElement, options); } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => + Serialize(writer, ModelSerializerOptions.DefaultWireOptions); } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/RoundTripStrategy.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/RoundTripStrategy.cs index b6f2bdb79301..63a8b08ffd1e 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/RoundTripStrategy.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/RoundTripStrategy.cs @@ -70,46 +70,6 @@ public override object Deserialize(string payload, object model, ModelSerializer } } - public class XmlInterfaceStrategy : RoundTripStrategy where T : class, IModelSerializable - { - public override bool IsExplicitJsonSerialize => false; - public override bool IsExplicitJsonDeserialize => false; - - public override BinaryData Serialize(T model, ModelSerializerOptions options) - { - using var stream = new MemoryStream(); - using var writer = XmlWriter.Create(stream); - ((IModelXmlSerializable)model).Serialize(writer, options); - writer.Flush(); - return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); - } - - public override object Deserialize(string payload, object model, ModelSerializerOptions options) - { - return ((IModelXmlSerializable)model).Deserialize(new BinaryData(Encoding.UTF8.GetBytes(payload)), options); - } - } - - public class XmlInterfaceNonGenericStrategy : RoundTripStrategy where T : class, IModelSerializable - { - public override bool IsExplicitJsonSerialize => false; - public override bool IsExplicitJsonDeserialize => false; - - public override BinaryData Serialize(T model, ModelSerializerOptions options) - { - using var stream = new MemoryStream(); - using var writer = XmlWriter.Create(stream); - ((IModelXmlSerializable)model).Serialize(writer, options); - writer.Flush(); - return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); - } - - public override object Deserialize(string payload, object model, ModelSerializerOptions options) - { - return ((IModelXmlSerializable)model).Deserialize(new BinaryData(Encoding.UTF8.GetBytes(payload)), options); - } - } - public class ModelInterfaceStrategy : RoundTripStrategy where T : class, IModelSerializable { public override bool IsExplicitJsonSerialize => false; @@ -270,39 +230,5 @@ public override object Deserialize(string payload, object model, ModelSerializer return _fromResponse(response); } } - - public class XmlInterfaceXElementStrategy : RoundTripStrategy where T : class, IModelSerializable - { - public override bool IsExplicitJsonSerialize => false; - public override bool IsExplicitJsonDeserialize => false; - - public override BinaryData Serialize(T model, ModelSerializerOptions options) - { - return ((IModelXmlSerializable)model).Serialize(options); - } - - public override object Deserialize(string payload, object model, ModelSerializerOptions options) - { - var stream = new MemoryStream(Encoding.UTF8.GetBytes(payload)); - return ((IModelXmlSerializable)model).Deserialize(XElement.Load(stream), options); - } - } - - public class XmlInterfaceXElementNonGenericStrategy : RoundTripStrategy where T : class, IModelSerializable - { - public override bool IsExplicitJsonSerialize => false; - public override bool IsExplicitJsonDeserialize => false; - - public override BinaryData Serialize(T model, ModelSerializerOptions options) - { - return ((IModelXmlSerializable)model).Serialize(options); - } - - public override object Deserialize(string payload, object model, ModelSerializerOptions options) - { - var stream = new MemoryStream(Encoding.UTF8.GetBytes(payload)); - return ((IModelXmlSerializable)model).Deserialize(XElement.Load(stream), options); - } - } } #pragma warning restore SA1402 // File may only contain a single type diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.Serialization.cs index a7d01b25fcd8..dc664c9eec03 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.Serialization.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.Serialization.cs @@ -7,17 +7,11 @@ using System; using System.Collections.Generic; -using System.Diagnostics.Tracing; -using System.IO; -using System.Runtime.CompilerServices; -using System.Text; using System.Text.Json; -using Azure.Core; using Azure.Core.Serialization; using Azure.Core.Tests.Public.ResourceManager.Compute.Models; using Azure.Core.Tests.Public.ResourceManager.Models; using Azure.Core.Tests.Public.ResourceManager.Resources.Models; -using Newtonsoft.Json.Linq; namespace Azure.Core.Tests.Public.ResourceManager.Compute { @@ -25,7 +19,12 @@ public partial class AvailabilitySetData : IUtf8JsonSerializable, IModelJsonSeri { void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IModelJsonSerializable)this).Serialize(writer, ModelSerializerOptions.DefaultWireOptions); - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + Serialize(writer, options); + } private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { @@ -241,6 +240,8 @@ public static AvailabilitySetData DeserializeAvailabilitySetData(JsonElement ele AvailabilitySetData IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + using var doc = JsonDocument.Parse(data); return DeserializeAvailabilitySetData(doc.RootElement, options); } @@ -266,6 +267,8 @@ private struct AvailabilitySetDataProperties AvailabilitySetData IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (!reader.TryDeserialize(options, SetProperty, out var properties)) return null; @@ -394,6 +397,11 @@ private static void SetProperty(ReadOnlySpan propertyName, ref Availabilit reader.Skip(); } - BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializer.ConvertToBinaryData(this, options); + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return ModelSerializer.ConvertToBinaryData(this, options); + } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.Serialization.cs index dcc121976819..118347a06bdf 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.Serialization.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.Serialization.cs @@ -88,6 +88,8 @@ public static ResourceProviderData DeserializeResourceProviderData(JsonElement e ResourceProviderData IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + using var doc = JsonDocument.Parse(data); return DeserializeResourceProviderData(doc.RootElement, options); } @@ -97,7 +99,12 @@ ResourceProviderData IModelSerializable.Deserialize(Binary // only used for public access to internal serialize public void Serialize(Utf8JsonWriter writer) => ((IUtf8JsonSerializable)this).Write(writer); - void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + Serialize(writer, options); + } private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) { @@ -152,6 +159,8 @@ private struct ResourceProviderDataProperties ResourceProviderData IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) { + ModelSerializerHelper.ValidateFormat(this, options.Format); + if (!reader.TryDeserialize< ResourceProviderDataProperties>(options, SetProperty, out var properties)) return null; @@ -211,6 +220,11 @@ private static void SetProperty(ReadOnlySpan propertyName, ref ResourcePro reader.Skip(); } - BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializer.ConvertToBinaryData(this, options); + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + ModelSerializerHelper.ValidateFormat(this, options.Format); + + return ModelSerializer.ConvertToBinaryData(this, options); + } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ModelXml.xml b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ModelXml.xml index ee71bb965ac1..2d6c52e2cc82 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ModelXml.xml +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ModelXml.xml @@ -1,5 +1,4 @@ - - + Color Red ReadOnly diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ModelXmlX.xml b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ModelXmlX.xml new file mode 100644 index 000000000000..a6c7329e21d8 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ModelXmlX.xml @@ -0,0 +1,9 @@ + + Color + Red + ReadOnly + + ChildRed + ChildReadOnly + +