diff --git a/sdk/core/Azure.Core/api/Azure.Core.net461.cs b/sdk/core/Azure.Core/api/Azure.Core.net461.cs index df35ebfa055c..768fbac5bc6d 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net461.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net461.cs @@ -1102,21 +1102,14 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } - public partial interface IJsonSerializableModel - { - void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } public partial interface IModelSerializable { - System.IO.Stream Serialize(Azure.Core.Serialization.ModelSerializerOptions options); - } - public partial interface IXmlSerializableModel - { - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { @@ -1136,24 +1129,19 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } - public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter { public ModelJsonConverter() { } public ModelJsonConverter(bool ignoreAdditionalProperties) { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public override bool CanConvert(System.Type typeToConvert) { throw null; } - public override Azure.Core.Serialization.IJsonSerializableModel Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } - public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonSerializableModel value, System.Text.Json.JsonSerializerOptions options) { } + public override Azure.Core.Serialization.IModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } } public static partial class ModelSerializer { - public static T DeserializeJson(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeJson(string json, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeXml(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } - public static System.IO.Stream SerializeJson(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static System.IO.Stream SerializeXml(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static System.IO.Stream Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } } public partial class ModelSerializerOptions { 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 720d4c9687db..a49d92ac5396 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs @@ -1102,21 +1102,14 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } - public partial interface IJsonSerializableModel - { - void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } public partial interface IModelSerializable { - System.IO.Stream Serialize(Azure.Core.Serialization.ModelSerializerOptions options); - } - public partial interface IXmlSerializableModel - { - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { @@ -1136,24 +1129,19 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } - public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter { public ModelJsonConverter() { } public ModelJsonConverter(bool ignoreAdditionalProperties) { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public override bool CanConvert(System.Type typeToConvert) { throw null; } - public override Azure.Core.Serialization.IJsonSerializableModel Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } - public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonSerializableModel value, System.Text.Json.JsonSerializerOptions options) { } + public override Azure.Core.Serialization.IModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } } public static partial class ModelSerializer { - public static T DeserializeJson(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeJson(string json, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeXml(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } - public static System.IO.Stream SerializeJson(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static System.IO.Stream SerializeXml(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static System.IO.Stream Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } } public partial class ModelSerializerOptions { 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 720d4c9687db..a49d92ac5396 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs @@ -1102,21 +1102,14 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } - public partial interface IJsonSerializableModel - { - void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } public partial interface IModelSerializable { - System.IO.Stream Serialize(Azure.Core.Serialization.ModelSerializerOptions options); - } - public partial interface IXmlSerializableModel - { - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { @@ -1136,24 +1129,19 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } - public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter { public ModelJsonConverter() { } public ModelJsonConverter(bool ignoreAdditionalProperties) { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public override bool CanConvert(System.Type typeToConvert) { throw null; } - public override Azure.Core.Serialization.IJsonSerializableModel Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } - public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonSerializableModel value, System.Text.Json.JsonSerializerOptions options) { } + public override Azure.Core.Serialization.IModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } } public static partial class ModelSerializer { - public static T DeserializeJson(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeJson(string json, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeXml(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } - public static System.IO.Stream SerializeJson(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static System.IO.Stream SerializeXml(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static System.IO.Stream Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } } public partial class ModelSerializerOptions { 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 df35ebfa055c..768fbac5bc6d 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs @@ -1102,21 +1102,14 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } - public partial interface IJsonSerializableModel - { - void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } public partial interface IModelSerializable { - System.IO.Stream Serialize(Azure.Core.Serialization.ModelSerializerOptions options); - } - public partial interface IXmlSerializableModel - { - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { @@ -1136,24 +1129,19 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } - public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter { public ModelJsonConverter() { } public ModelJsonConverter(bool ignoreAdditionalProperties) { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public override bool CanConvert(System.Type typeToConvert) { throw null; } - public override Azure.Core.Serialization.IJsonSerializableModel Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } - public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonSerializableModel value, System.Text.Json.JsonSerializerOptions options) { } + public override Azure.Core.Serialization.IModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } } public static partial class ModelSerializer { - public static T DeserializeJson(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeJson(string json, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeXml(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } - public static System.IO.Stream SerializeJson(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static System.IO.Stream SerializeXml(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static System.IO.Stream Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } } public partial class ModelSerializerOptions { 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 df35ebfa055c..768fbac5bc6d 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs @@ -1102,21 +1102,14 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } - public partial interface IJsonSerializableModel - { - void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); - } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } public partial interface IModelSerializable { - System.IO.Stream Serialize(Azure.Core.Serialization.ModelSerializerOptions options); - } - public partial interface IXmlSerializableModel - { - void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { @@ -1136,24 +1129,19 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } - public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter { public ModelJsonConverter() { } public ModelJsonConverter(bool ignoreAdditionalProperties) { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public override bool CanConvert(System.Type typeToConvert) { throw null; } - public override Azure.Core.Serialization.IJsonSerializableModel Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } - public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonSerializableModel value, System.Text.Json.JsonSerializerOptions options) { } + public override Azure.Core.Serialization.IModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } } public static partial class ModelSerializer { - public static T DeserializeJson(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeJson(string json, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static T DeserializeXml(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } - public static System.IO.Stream SerializeJson(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IJsonSerializableModel { throw null; } - public static System.IO.Stream SerializeXml(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IXmlSerializableModel { throw null; } - public static System.IO.Stream Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions? options = null) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } } public partial class ModelSerializerOptions { diff --git a/sdk/core/Azure.Core/samples/Serialization.md b/sdk/core/Azure.Core/samples/Serialization.md index 1340d8fcd205..c6a9c786b538 100644 --- a/sdk/core/Azure.Core/samples/Serialization.md +++ b/sdk/core/Azure.Core/samples/Serialization.md @@ -26,66 +26,8 @@ Console.WriteLine(dog.IsHungry); Given that explicit cast does not allow for serialization options we might also consider a static `FromResponse` and instance `ToRequestContent` methods. -## Using System.Text.Json - -In order to better integrate with the rest of the .NET ecosystem, Azure.Core supports System.Text.Json serialization. The following example demonstrates using System.Text.Json for serialization and deserialization -If we go this route the IJsonSerializable interface will only be needed for compile time constraints and can most likely be methodless and renamed to IRehydratable. - -One limitation if we go this route is there isn't a clear place to pass in a flag to include additional properties during serialization and deserialization. -One solution here is to always have this on by default for public usage and internally turn it off for communication with Azure services. - -### Serialization - -```C# Snippet:Stj_Serialize -DogListProperty dog = new DogListProperty -{ - Name = "Doggo", - IsHungry = false, - Weight = 1.1, - FoodConsumed = { "kibble", "egg", "peanut butter" }, -}; - -//STJ example -string json = System.Text.Json.JsonSerializer.Serialize(dog); -``` - -### Deserialization - -```C# Snippet:Stj_Deserialize -string json = "{\"latinName\":\"Animalia\",\"weight\":1.1,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; - -//stj example -DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json); -``` - -## Using ModelSerializer - -Serialize would use the Try/Do examples from above. We would use Interface form the Serializable but potentially have static method for Deserialize. -When using Static Deserialize, an empty Model does not have to be created first as we can deserialize directly into a new instance. - -### Serialization - -```C# Snippet:ModelSerializer_Serialize -DogListProperty dog = new DogListProperty -{ - Name = "Doggo", - IsHungry = true, - Weight = 1.1, - FoodConsumed = { "kibble", "egg", "peanut butter" }, -}; - -Stream stream = ModelSerializer.SerializeJson(dog); -``` - -### Deserialization - -```C# Snippet:ModelSerializer_Deserialize -string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; - -DogListProperty dog = ModelSerializer.DeserializeJson(json); -``` - ## Using ModelSerializer for NewtonSoftJson + By using the ModelSerializer class, a new instance of Dog does not need to be created before calling Deserialize. Also added ObjectSerializer to Options class so different kinds of Serializers can be used. ### Serialization @@ -101,7 +43,7 @@ DogListProperty dog = new DogListProperty ModelSerializerOptions options = new ModelSerializerOptions(); options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); -Stream stream = ModelSerializer.SerializeJson(dog, options); +BinaryData data = ModelSerializer.Serialize(dog, options); ``` ### Deserialization @@ -111,10 +53,16 @@ ModelSerializerOptions options = new ModelSerializerOptions(); options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; -DogListProperty dog = ModelSerializer.DeserializeJson(json, options); +DogListProperty dog = ModelSerializer.Deserialize(BinaryData.FromString(json), options); ``` ## Using ModelJsonConverter for JsonSerializer + +In order to better integrate with the rest of the .NET ecosystem, Azure.Core supports System.Text.Json serialization. The following example demonstrates using System.Text.Json for serialization and deserialization +If we go this route the IJsonSerializable interface will only be needed for compile time constraints and can most likely be methodless and renamed to IRehydratable. + +One limitation if we go this route is there isn't a clear place to pass in a flag to include additional properties during serialization and deserialization. + By using the ModelJsonConverter class we can have a place to add additional properties to the JsonSerializerOptions. This will allow us to add things like `IgnoreAdditionalProperties` and `Version` to the options without needing to have our own ModelSerializer. The `SerializableOptions` would become internal and we would have a converter to convert from `JsonSerializerOptions` + `ModelJsonConverter` to `SerializableOptions`. @@ -148,6 +96,7 @@ DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize model = ModelSerializer.DeserializeJson>(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); -``` - -## XmlModel Example -By using the SerializeXml and DeserializeXml methods we can serialize and deserialize Xml Models using the XmlSerializer. Next steps include combining ModelSerializer Serialize/Deserialize methods with XmlSerializer models to have a single method for both Json and Xml. - -### Serialization - -```C# Snippet:XmlModelSerialize -ModelXml modelXml = new ModelXml("Color", "Red", "ReadOnly"); -var stream = ModelSerializer.SerializeXml(modelXml); -stream.Position = 0; -string roundTrip = new StreamReader(stream).ReadToEnd(); -``` - -### Deserialization - -```C# Snippet:XmlModelDeserialize -string serviceResponse = - "" + - "Color" + - "Red" + - ""; - -ModelXml model = ModelSerializer.DeserializeXml(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse))); +Envelope model = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); ``` ## Using ModelSerializer with generic IModelSerializable @@ -220,22 +145,20 @@ introduce a question around what happens if a model could be serialized as both ```C# Snippet:ModelSerializer_IModelSerializable_Serialize XmlModelForCombinedInterface xmlModel = new XmlModelForCombinedInterface("Color", "Red", "ReadOnly"); -var stream = ModelSerializer.Serialize(xmlModel); -stream.Position = 0; -string xmlString = new StreamReader(stream).ReadToEnd(); +var data = ModelSerializer.Serialize(xmlModel); +string xmlString = data.ToString(); JsonModelForCombinedInterface jsonModel = new JsonModelForCombinedInterface("Color", "Red", "ReadOnly"); -stream = ModelSerializer.Serialize(jsonModel); -stream.Position = 0; -string jsonString = new StreamReader(stream).ReadToEnd(); +data = ModelSerializer.Serialize(jsonModel); +string jsonString = data.ToString(); ``` ### Deserialization ```C# Snippet:ModelSerializer_IModelSerializable_Deserialize string xmlResponse = "ColorRed"; -XmlModelForCombinedInterface xmlModel = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(xmlResponse))); +XmlModelForCombinedInterface xmlModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(xmlResponse))); string jsonResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"x\":\"extra\"}"; -JsonModelForCombinedInterface jsonModel = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(jsonResponse))); +JsonModelForCombinedInterface jsonModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(jsonResponse))); ``` diff --git a/sdk/core/Azure.Core/src/Serialization/IJsonSerializableModel.cs b/sdk/core/Azure.Core/src/Serialization/IJsonSerializableModel.cs deleted file mode 100644 index af28f066acad..000000000000 --- a/sdk/core/Azure.Core/src/Serialization/IJsonSerializableModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Data; -using System.Text.Json; -using System.Xml; -using Azure.Core.Serialization; - -namespace Azure.Core.Serialization -{ - /// - /// TODO - /// - public interface IJsonSerializableModel - { - /// - /// . - /// - /// - /// -#pragma warning disable AZC0014 // Avoid using banned types in public API - void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options); -#pragma warning restore AZC0014 // Avoid using banned types in public API - } -} diff --git a/sdk/core/Azure.Core/src/Serialization/IModelSerializable.cs b/sdk/core/Azure.Core/src/Serialization/IModelSerializable.cs index a271845dcad2..1587d21d77d3 100644 --- a/sdk/core/Azure.Core/src/Serialization/IModelSerializable.cs +++ b/sdk/core/Azure.Core/src/Serialization/IModelSerializable.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.IO; namespace Azure.Core.Serialization @@ -13,8 +14,14 @@ public interface IModelSerializable /// /// . /// -#pragma warning disable AZC0014 // Avoid using banned types in public API - Stream Serialize(ModelSerializerOptions options); -#pragma warning restore AZC0014 // Avoid using banned types in public API + BinaryData Serialize(ModelSerializerOptions options); + + /// + /// . + /// + /// + /// + /// + object Deserialize(BinaryData data, ModelSerializerOptions options); } } diff --git a/sdk/core/Azure.Core/src/Serialization/IXmlSerializableModel.cs b/sdk/core/Azure.Core/src/Serialization/IXmlSerializableModel.cs deleted file mode 100644 index 3ecd2555ccd4..000000000000 --- a/sdk/core/Azure.Core/src/Serialization/IXmlSerializableModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Xml; - -namespace Azure.Core.Serialization -{ - /// - /// TODO - /// - public interface IXmlSerializableModel - { - /// - /// . - /// - /// - /// - void Serialize(XmlWriter writer, ModelSerializerOptions options); - } -} diff --git a/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs b/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs index f440e6b455b4..f1635b8e9e63 100644 --- a/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs +++ b/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs @@ -13,7 +13,7 @@ namespace Azure.Core.Serialization /// . /// #pragma warning disable AZC0014 // Avoid using banned types in public API - public class ModelJsonConverter : JsonConverter + public class ModelJsonConverter : JsonConverter #pragma warning restore AZC0014 // Avoid using banned types in public API { /// @@ -54,14 +54,10 @@ public override bool CanConvert(Type typeToConvert) /// /// #pragma warning disable AZC0014 // Avoid using banned types in public API - public override IJsonSerializableModel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override IModelSerializable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) #pragma warning restore AZC0014 // Avoid using banned types in public API { - var model = ModelSerializer.DeserializeObjectJson(JsonDocument.ParseValue(ref reader).RootElement, typeToConvert, ConvertOptions(options)) as IJsonSerializableModel; - if (model is null) - throw new InvalidOperationException($"Unexpected error when deserializing {typeToConvert.Name}."); - - return model; + return (IModelSerializable)ModelSerializer.Deserialize(BinaryData.FromString(JsonDocument.ParseValue(ref reader).RootElement.GetRawText()), typeToConvert, ConvertOptions(options)); } /// @@ -71,10 +67,15 @@ public override IJsonSerializableModel Read(ref Utf8JsonReader reader, Type type /// /// #pragma warning disable AZC0014 // Avoid using banned types in public API - public override void Write(Utf8JsonWriter writer, IJsonSerializableModel value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, IModelSerializable value, JsonSerializerOptions options) #pragma warning restore AZC0014 // Avoid using banned types in public API { - value.Serialize(writer, ConvertOptions(options)); + BinaryData data = value.Serialize(ConvertOptions(options)); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif } private ModelSerializerOptions ConvertOptions(JsonSerializerOptions options) diff --git a/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs index 498ac3de030f..d1ebde094572 100644 --- a/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs +++ b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs @@ -4,10 +4,6 @@ using System; using System.IO; using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Xml; -using System.Xml.Linq; namespace Azure.Core.Serialization { @@ -16,7 +12,6 @@ namespace Azure.Core.Serialization /// public static class ModelSerializer { - #region JsonSerialize /// /// Serialize a model. /// @@ -24,7 +19,7 @@ public static class ModelSerializer /// /// /// - public static Stream SerializeJson(T model, ModelSerializerOptions? options = default) where T : class, IJsonSerializableModel + public static BinaryData Serialize(T model, ModelSerializerOptions? options = default) where T : class, IModelSerializable { // if options.Serializers is set and the model is in the dictionary, use the serializer if (options != null) @@ -33,256 +28,52 @@ public static Stream SerializeJson(T model, ModelSerializerOptions? options = if (options.Serializers.TryGetValue(typeof(T), out serializer)) { - BinaryData data = serializer.Serialize(model); - return data.ToStream(); - } - } - // else use default STJ serializer - Stream stream = new MemoryStream(); - var writer = new Utf8JsonWriter(stream); - model.Serialize(writer, options ?? new ModelSerializerOptions()); - writer.Flush(); - return stream; - } - #endregion - - #region XmlSerialize - /// - /// Serialize a XML model. Todo: collapse this method when working - need compile check over runtime - /// - /// - /// - /// - /// - public static Stream SerializeXml(T model, ModelSerializerOptions? options = default) where T : class, IXmlSerializableModel - { - // if options.Serializers is set and the model is in the dictionary, use the serializer - if (options != null) - { - ObjectSerializer? serializer; - - if (options.Serializers.TryGetValue(typeof(T), out serializer)) - { - BinaryData data = serializer.Serialize(model); - return data.ToStream(); - } - } - // else use default XmlWriter - Stream stream = new MemoryStream(); - var writer = XmlWriter.Create(stream, new XmlWriterSettings - { - Encoding = new UTF8Encoding(false), - OmitXmlDeclaration = true, - Indent = true - }); - - model.Serialize(writer, options ?? new ModelSerializerOptions()); - writer.Flush(); - return stream; - } - #endregion - - #region GenericSerialize - /// - /// Serialize a model. - /// - /// - /// - /// - /// - public static Stream Serialize(T model, ModelSerializerOptions? options = default) where T : class, IModelSerializable - { - // if options.Serializers is set and the model is in the dictionary, use the serializer - if (options != null) - { - ObjectSerializer? serializer; - - if (options.Serializers.TryGetValue(typeof(T), out serializer)) - { - BinaryData data = serializer.Serialize(model); - return data.ToStream(); + return serializer.Serialize(model); } } return model.Serialize(options ?? new ModelSerializerOptions()); } - #endregion - #region XmlDeserialize /// /// Serialize a XML model. Todo: collapse this method when working - need compile check over runtime /// /// - public static T DeserializeXml(Stream stream, ModelSerializerOptions? options = default) where T : class, IXmlSerializableModel - { - if (options != null) - { - ObjectSerializer? serializer; - - if (options.Serializers.TryGetValue(typeof(T), out serializer)) - { - var obj = serializer.Deserialize(stream, typeof(T), default); - if (obj is null) - throw new InvalidOperationException(); - else - return (T)obj; - } - } - - return DeserializeWithReflectionXml(XElement.Load(stream), options); - } - - private static T DeserializeWithReflectionXml(XElement xmlElement, ModelSerializerOptions? options) where T : class, IXmlSerializableModel - { - Type typeToConvert = typeof(T); - options ??= new ModelSerializerOptions(); - - T? model = DeserializeObjectXml(xmlElement, typeToConvert, options) as T; - if (model is null) - throw new InvalidOperationException($"Unexpected error when deserializing {typeToConvert.Name}."); - - return model; - } - - internal static object? DeserializeObjectXml(XElement xmlElement, Type typeToConvert, ModelSerializerOptions options) - { - var classNameInMethod = typeToConvert.Name.AsSpan(); - int backtickIndex = classNameInMethod.IndexOf('`'); - if (backtickIndex != -1) - classNameInMethod = classNameInMethod.Slice(0, backtickIndex); - var methodName = $"Deserialize{classNameInMethod.ToString()}"; - - var method = typeToConvert.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); - if (method is null) - throw new NotSupportedException($"{typeToConvert.Name} does not have a deserialize method defined."); - - return method.Invoke(null, new object[] { xmlElement, options }); - } - #endregion - - #region JsonDeserialize - /// - /// Deserialize a model. - /// - /// - /// - /// - /// - public static T DeserializeJson(string json, ModelSerializerOptions? options = default) where T : class, IJsonSerializableModel + public static T Deserialize(BinaryData data, ModelSerializerOptions? options = default) where T : class, IModelSerializable { - using Stream stream = new MemoryStream(); - using StreamWriter writer = new StreamWriter(stream); - writer.Write(json); - stream.Position = 0; - ObjectSerializer? serializer; - - if (options != null) - { - if (options.Serializers.TryGetValue(typeof(T), out serializer)) - { - var obj = serializer.Deserialize(stream, typeof(T), default); - if (obj is null) - throw new InvalidOperationException(); - else - return (T)obj; - } - } - - return DeserializeWithReflectionJson(JsonDocument.Parse(stream).RootElement, options); + return (T)Deserialize(data, typeof(T), options); } - /// - /// Deserialize a model. - /// - /// - /// - /// - /// - public static T DeserializeJson(Stream stream, ModelSerializerOptions? options = default) where T : class, IJsonSerializableModel + internal static object Deserialize(BinaryData data, Type typeToConvert, ModelSerializerOptions? options = default) { if (options != null) { ObjectSerializer? serializer; - if (options.Serializers.TryGetValue(typeof(T), out serializer)) + if (options.Serializers.TryGetValue(typeToConvert, out serializer)) { - var obj = serializer.Deserialize(stream, typeof(T), default); + var obj = serializer.Deserialize(data.ToStream(), typeToConvert, default); if (obj is null) throw new InvalidOperationException(); else - return (T)obj; + return obj; } } - return DeserializeWithReflectionJson(JsonDocument.Parse(stream).RootElement, options); - } - - private static T DeserializeWithReflectionJson(JsonElement rootElement, ModelSerializerOptions? options) where T : class, IJsonSerializableModel - { - Type typeToConvert = typeof(T); options ??= new ModelSerializerOptions(); - T? model = DeserializeObjectJson(rootElement, typeToConvert, options) as T; - if (model is null) - throw new InvalidOperationException($"Unexpected error when deserializing {typeToConvert.Name}."); - - return model; - } - - internal static object? DeserializeObjectJson(JsonElement rootElement, Type typeToConvert, ModelSerializerOptions options) - { - var classNameInMethod = typeToConvert.Name.AsSpan(); - int backtickIndex = classNameInMethod.IndexOf('`'); - if (backtickIndex != -1) - classNameInMethod = classNameInMethod.Slice(0, backtickIndex); - var methodName = $"Deserialize{classNameInMethod.ToString()}"; - - var method = typeToConvert.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); - if (method is null) - throw new NotSupportedException($"{typeToConvert.Name} does not have a deserialize method defined."); - - return method.Invoke(null, new object[] { rootElement, options }); - } - #endregion - - #region GenericDeserialize - /// - /// Serialize a XML model. Todo: collapse this method when working - need compile check over runtime - /// - /// - public static T Deserialize(Stream stream, ModelSerializerOptions? options = default) where T : class, IModelSerializable - { - if (options != null) - { - ObjectSerializer? serializer; - - if (options.Serializers.TryGetValue(typeof(T), out serializer)) - { - var obj = serializer.Deserialize(stream, typeof(T), default); - if (obj is null) - throw new InvalidOperationException(); - else - return (T)obj; - } - } - - return DeserializeWithReflection(stream, options); - } - - private static T DeserializeWithReflection(Stream stream, ModelSerializerOptions? options) where T : class, IModelSerializable - { - Type typeToConvert = typeof(T); - options ??= new ModelSerializerOptions(); + if (typeToConvert.IsAbstract) + return DeserializeObject(data, typeToConvert, options); - T? model = DeserializeObject(stream, typeToConvert, options) as T; + IModelSerializable? model = Activator.CreateInstance(typeToConvert, true) as IModelSerializable; if (model is null) - throw new InvalidOperationException($"Unexpected error when deserializing {typeToConvert.Name}."); + throw new InvalidOperationException($"{typeToConvert.Name} does not implement {nameof(IModelSerializable)}."); - return model; + return model.Deserialize(data, options); } - private static readonly Type[] _combinedDeserializeMethodParameters = new Type[] { typeof(Stream), typeof(ModelSerializerOptions) }; + private static readonly Type[] _combinedDeserializeMethodParameters = new Type[] { typeof(BinaryData), typeof(ModelSerializerOptions) }; - internal static object? DeserializeObject(Stream stream, Type typeToConvert, ModelSerializerOptions options) + internal static object DeserializeObject(BinaryData data, Type typeToConvert, ModelSerializerOptions options) { var classNameInMethod = typeToConvert.Name.AsSpan(); int backtickIndex = classNameInMethod.IndexOf('`'); @@ -294,8 +85,7 @@ private static T DeserializeWithReflection(Stream stream, ModelSerializerOpti if (method is null) throw new NotSupportedException($"{typeToConvert.Name} does not have a deserialize method defined."); - return method.Invoke(null, new object[] { stream, options }); + return method.Invoke(null, new object[] { data, options })!; } - #endregion } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/CombinedInterfaceTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/CombinedInterfaceTests.cs index a9bd0a12df06..a1ce4169c01f 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/CombinedInterfaceTests.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/CombinedInterfaceTests.cs @@ -15,13 +15,11 @@ internal class CombinedInterfaceTests { [TestCase(true)] [TestCase(false)] - public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnlyProperties) + public void CanRoundTripFutureVersionWithoutLossXml(bool ignoreReadOnlyProperties) { ModelSerializerOptions options = new ModelSerializerOptions(); options.IgnoreReadOnlyProperties = ignoreReadOnlyProperties; - Stream stream = new MemoryStream(); - string serviceResponse = "" + "Color" + @@ -29,23 +27,22 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnlyProperties) "ReadOnly" + ""; - var expectedSerializedString = "ColorRed"; + var expectedSerializedString = "\uFEFFColorRed"; if (!ignoreReadOnlyProperties) expectedSerializedString += "ReadOnly"; expectedSerializedString += ""; - XmlModelForCombinedInterface model = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options); + XmlModelForCombinedInterface model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options); Assert.AreEqual("Color", model.Key); Assert.AreEqual("Red", model.Value); Assert.AreEqual("ReadOnly", model.ReadOnlyProperty); - stream = ModelSerializer.Serialize(model, options); - stream.Position = 0; - string roundTrip = new StreamReader(stream).ReadToEnd(); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); - XmlModelForCombinedInterface model2 = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(roundTrip)), options); + XmlModelForCombinedInterface model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options); VerifyModelXmlModelForCombinedInterface(model, model2, ignoreReadOnlyProperties); } @@ -59,8 +56,6 @@ public void CanRoundTripFutureVersionWithoutLossJson(bool ignoreReadOnlyProperti options.IgnoreAdditionalProperties = ignoreAdditionalProperties; options.IgnoreReadOnlyProperties = ignoreReadOnlyProperties; - Stream stream = new MemoryStream(); - string serviceResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"x\":\"extra\"}"; var expectedSerializedString = "{\"key\":\"Color\",\"value\":\"Red\""; @@ -70,7 +65,7 @@ public void CanRoundTripFutureVersionWithoutLossJson(bool ignoreReadOnlyProperti expectedSerializedString += ",\"x\":\"extra\""; expectedSerializedString += "}"; - JsonModelForCombinedInterface model = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options); + JsonModelForCombinedInterface model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options); Assert.AreEqual("Color", model.Key); Assert.AreEqual("Red", model.Value); @@ -78,13 +73,12 @@ public void CanRoundTripFutureVersionWithoutLossJson(bool ignoreReadOnlyProperti var additionalProperties = typeof(JsonModelForCombinedInterface).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(model) as Dictionary; Assert.IsNotNull(additionalProperties); Assert.AreEqual(!ignoreAdditionalProperties, additionalProperties.ContainsKey("x")); - stream = ModelSerializer.Serialize(model, options); - stream.Position = 0; - string roundTrip = new StreamReader(stream).ReadToEnd(); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); - JsonModelForCombinedInterface model2 = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(roundTrip)), options); + JsonModelForCombinedInterface model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options); VerifyModelJsonModelForCombinedInterface(model, model2, ignoreReadOnlyProperties, ignoreAdditionalProperties); } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs index 8118c9b1ca48..4ad575cb800a 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs @@ -7,10 +7,12 @@ using System.Text; using System.Text.Json; using Azure.Core.Serialization; +using Microsoft.Extensions.Options; +using Newtonsoft.Json.Linq; namespace Azure.Core.Tests.Public.ModelSerializationTests { - public class Envelope : IJsonSerializableModel, IUtf8JsonSerializable + public class Envelope : IModelSerializable, IUtf8JsonSerializable { private Dictionary RawData { get; set; } = new Dictionary(); @@ -37,10 +39,20 @@ internal Envelope(string property, CatReadOnlyProperty cat, T modelC, Dictionary public T ModelT { get; set; } #region Serialization - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); if (!options.IgnoreReadOnlyProperties) { @@ -49,7 +61,12 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } writer.WritePropertyName("modelA"u8); - ((IJsonSerializableModel)ModelA).Serialize(writer, options); + BinaryData data = ((IModelSerializable)ModelA).Serialize(options); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif writer.WritePropertyName("modelC"u8); SerializeT(writer, options); @@ -67,6 +84,9 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } internal static Envelope DeserializeEnvelope(JsonElement element, ModelSerializerOptions options) @@ -136,6 +156,11 @@ private static T DeserializeT(JsonElement element, ModelSerializerOptions option m.Position = 0; return (T)serializer.Deserialize(m, typeof(T), default); } -#endregion + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeEnvelope(JsonDocument.Parse(data.ToString()).RootElement, options); + } + #endregion } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/EnvelopeTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/EnvelopeTests.cs index a838a97322d4..6177ea12de69 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/EnvelopeTests.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/EnvelopeTests.cs @@ -6,6 +6,7 @@ using Azure.Core.Serialization; using NUnit.Framework; using Newtonsoft.Json; +using System; namespace Azure.Core.Tests.Public.ModelSerializationTests { @@ -18,7 +19,6 @@ internal class EnvelopeTests [TestCase(false)] public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) { - Stream stream = new MemoryStream(); string serviceResponse = "{\"readOnlyProperty\":\"read\"," + "\"modelA\":{\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5}," + @@ -53,7 +53,7 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) else options.Serializers.Add(typeof(ModelC), new NewtonsoftJsonObjectSerializer()); - Envelope model = ModelSerializer.DeserializeJson>(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); + Envelope model = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); if (!ignoreReadOnly) { @@ -64,13 +64,12 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) VerifyModels.CheckAnimals(correctCat, model.ModelA, options); Assert.AreEqual("hello", model.ModelT.X); Assert.AreEqual("bye", model.ModelT.Y); - stream = ModelSerializer.SerializeJson(model, options); - stream.Position = 0; - string roundTrip = new StreamReader(stream).ReadToEnd(); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); - var model2 = ModelSerializer.DeserializeJson>(new MemoryStream(Encoding.UTF8.GetBytes(roundTrip)), options: options); + var model2 = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options: options); ModelC correctModelC = new ModelC("hello", "bye"); ModelC.VerifyModelC(correctModelC, model2.ModelT); } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs index 881e5407a189..eaa0e9630d17 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using Azure.Core.Tests.Public.ModelSerializationTests.Models; using Azure.Core.Serialization; +using System; namespace Azure.Core.Tests.Public.ModelSerializationTests { @@ -27,23 +28,22 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadonlyProperites) "ReadOnly" + ""; - var expectedSerializedString = "\r\n Color\r\n Red\r\n"; + var expectedSerializedString = "\uFEFFColorRed"; if (!ignoreReadonlyProperites) - expectedSerializedString += " ReadOnly\r\n"; + expectedSerializedString += "ReadOnly"; expectedSerializedString += ""; - ModelXml model = ModelSerializer.DeserializeXml(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options); + ModelXml model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options); Assert.AreEqual("Color", model.Key); Assert.AreEqual("Red", model.Value); Assert.AreEqual("ReadOnly", model.ReadOnlyProperty); - stream = ModelSerializer.SerializeXml(model, options); - stream.Position = 0; - string roundTrip = new StreamReader(stream).ReadToEnd(); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); - ModelXml model2 = ModelSerializer.DeserializeXml(new MemoryStream(Encoding.UTF8.GetBytes(roundTrip)), options); + ModelXml model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options); VerifyModelXml(model, model2, ignoreReadonlyProperites); } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Animal.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Animal.cs index 25b6bf58102e..0d652be91304 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Animal.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Animal.cs @@ -10,7 +10,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests { - public class Animal : IUtf8JsonSerializable, IJsonSerializableModel + public class Animal : IUtf8JsonSerializable, IModelSerializable { private Dictionary RawData { get; set; } = new Dictionary(); @@ -46,10 +46,20 @@ internal Animal(string name) } #region Serialization - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); if (!options.IgnoreReadOnlyProperties) { @@ -77,6 +87,9 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } internal static Animal DeserializeAnimal(JsonElement element, ModelSerializerOptions options) @@ -122,13 +135,9 @@ internal static Animal DeserializeAnimal(JsonElement element, ModelSerializerOpt #region InterfaceImplementation - private void CopyModel(Animal model) + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { - this.LatinName = model.LatinName; - this.Weight = model.Weight; - this.IsHungry = model.IsHungry; - this.Name = model.Name; - this.RawData = model.RawData; + return DeserializeAnimal(JsonDocument.Parse(data.ToString()).RootElement, options); } #endregion diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/CatReadOnlyProperty.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/CatReadOnlyProperty.cs index 753f87b58dab..add2d3a38e5a 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/CatReadOnlyProperty.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/CatReadOnlyProperty.cs @@ -9,7 +9,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests { - public class CatReadOnlyProperty : Animal, IJsonSerializableModel, IUtf8JsonSerializable + public class CatReadOnlyProperty : Animal, IModelSerializable, IUtf8JsonSerializable { private Dictionary RawData { get; set; } = new Dictionary(); @@ -30,10 +30,20 @@ internal CatReadOnlyProperty() public bool HasWhiskers { get; private set; } = true; #region Serialization - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer)=> ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); if (!options.IgnoreReadOnlyProperties) { @@ -64,6 +74,9 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } internal static CatReadOnlyProperty DeserializeCatReadOnlyProperty(JsonElement element, ModelSerializerOptions options) @@ -111,6 +124,12 @@ internal static CatReadOnlyProperty DeserializeCatReadOnlyProperty(JsonElement e } return new CatReadOnlyProperty(weight, latinName, name, isHungry, hasWhiskers, rawData); } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeCatReadOnlyProperty(JsonDocument.Parse(data.ToString()).RootElement, options); + } + #endregion } } 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 7242ba0cbbf5..765f9f401619 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 @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.Json; @@ -11,17 +12,27 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { - internal abstract class BaseModel : IUtf8JsonSerializable, IJsonSerializableModel + internal abstract class BaseModel : IUtf8JsonSerializable, IModelSerializable { private Dictionary RawData { get; set; } = new Dictionary(); public string Kind { get; internal set; } public string Name { get; set; } - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); writer.WritePropertyName("kind"u8); writer.WriteStringValue(Kind); @@ -44,8 +55,14 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } + internal static BaseModel DeserializeBaseModel(BinaryData data, ModelSerializerOptions options) + => DeserializeBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); + internal static BaseModel DeserializeBaseModel(JsonElement element, ModelSerializerOptions options = default) { if (element.ValueKind == JsonValueKind.Null) @@ -65,6 +82,7 @@ internal static BaseModel DeserializeBaseModel(JsonElement element, ModelSeriali return UnknownBaseModel.DeserializeUnknownBaseModel(element, options); } - protected abstract void CopyModel(BaseModel model); + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + => DeserializeBaseModel(JsonDocument.Parse(data.ToString()).RootElement, 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 98fecc7fee8c..be97f3bf6525 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 @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.Json; @@ -11,7 +12,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { - internal class ModelX : BaseModel, IUtf8JsonSerializable, IJsonSerializableModel + internal class ModelX : BaseModel, IUtf8JsonSerializable, IModelSerializable { private Dictionary RawData { get; set; } = new Dictionary(); @@ -30,10 +31,20 @@ internal ModelX(string kind, string name, int xProperty, Dictionary ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); writer.WritePropertyName("kind"u8); writer.WriteStringValue(Kind); @@ -61,6 +72,9 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } internal static ModelX DeserializeModelX(JsonElement element, ModelSerializerOptions options = default) @@ -99,13 +113,9 @@ internal static ModelX DeserializeModelX(JsonElement element, ModelSerializerOpt return new ModelX(kind, name, xProperty, rawData); } - protected override void CopyModel(BaseModel model) + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { - var that = model as ModelX; - this.Name = that.Name; - this.Kind = that.Kind; - this.XProperty = that.XProperty; - this.RawData = that.RawData; + return DeserializeModelX(JsonDocument.Parse(data.ToString()).RootElement, options); } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelY.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelY.cs index f27c732fde56..c3e25259e64c 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelY.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelY.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.Json; @@ -12,7 +13,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { - internal class ModelY : BaseModel, IUtf8JsonSerializable, IJsonSerializableModel + internal class ModelY : BaseModel, IUtf8JsonSerializable, IModelSerializable { private Dictionary RawData { get; set; } = new Dictionary(); @@ -31,10 +32,20 @@ internal ModelY(string kind, string name, string yProperty, Dictionary ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); writer.WritePropertyName("kind"u8); writer.WriteStringValue(Kind); @@ -62,6 +73,9 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } internal static ModelY DeserializeModelY(JsonElement element, ModelSerializerOptions options = default) @@ -100,13 +114,9 @@ internal static ModelY DeserializeModelY(JsonElement element, ModelSerializerOpt return new ModelY(kind, name, yProperty, rawData); } - protected override void CopyModel(BaseModel model) + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { - var that = model as ModelY; - this.Name = that.Name; - this.Kind = that.Kind; - this.YProperty = that.YProperty; - this.RawData = that.RawData; + return DeserializeModelY(JsonDocument.Parse(data.ToString()).RootElement, 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 0c6461d71d05..4626f9bfa321 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 @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Text.Json; @@ -12,7 +13,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { - internal class UnknownBaseModel : BaseModel, IUtf8JsonSerializable, IJsonSerializableModel + internal class UnknownBaseModel : BaseModel, IUtf8JsonSerializable, IModelSerializable { private Dictionary RawData { get; set; } = new Dictionary(); @@ -28,10 +29,20 @@ internal UnknownBaseModel(string kind, string name, Dictionary ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); writer.WritePropertyName("kind"u8); writer.WriteStringValue(Kind); @@ -54,6 +65,9 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } internal static UnknownBaseModel DeserializeUnknownBaseModel(JsonElement element, ModelSerializerOptions options = default) @@ -86,12 +100,9 @@ internal static UnknownBaseModel DeserializeUnknownBaseModel(JsonElement element return new UnknownBaseModel(kind, name, rawData); } - protected override void CopyModel(BaseModel model) + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) { - var that = model as UnknownBaseModel; - this.Name = that.Name; - this.Kind = that.Kind; - this.RawData = that.RawData; + return DeserializeUnknownBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListProperty.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListProperty.cs index cd129f897b45..0ce13560d93f 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListProperty.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListProperty.cs @@ -12,7 +12,7 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests { [JsonConverter(typeof(DogListPropertyConverter))] - public class DogListProperty : Animal, IJsonSerializableModel, IUtf8JsonSerializable + public class DogListProperty : Animal, IModelSerializable, IUtf8JsonSerializable { private Dictionary RawData { get; set; } = new Dictionary(); public IList FoodConsumed { get; private set; } @@ -55,10 +55,20 @@ public static explicit operator RequestContent(DogListProperty dog) } #region Serialization - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonSerializableModel)this).Serialize(writer, new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } - void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); if (!options.IgnoreReadOnlyProperties) { @@ -97,6 +107,9 @@ void IJsonSerializableModel.Serialize(Utf8JsonWriter writer, ModelSerializerOpti } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); } internal static DogListProperty DeserializeDogListProperty(JsonElement element, ModelSerializerOptions options) @@ -160,7 +173,12 @@ public override DogListProperty Read(ref Utf8JsonReader reader, Type typeToConve public override void Write(Utf8JsonWriter writer, DogListProperty value, JsonSerializerOptions options) { - ((IJsonSerializableModel)value).Serialize(writer, ConvertOptions(options)); + BinaryData data = ((IModelSerializable)value).Serialize(ConvertOptions(options)); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif } private ModelSerializerOptions ConvertOptions(JsonSerializerOptions options) @@ -174,5 +192,9 @@ private ModelSerializerOptions ConvertOptions(JsonSerializerOptions options) return serializableOptions; } } + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeDogListProperty(JsonDocument.Parse(data.ToString()).RootElement, options); + } } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/JsonModelForCombinedInterface.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/JsonModelForCombinedInterface.cs index eb579f958541..e1920998d3ba 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/JsonModelForCombinedInterface.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/JsonModelForCombinedInterface.cs @@ -15,6 +15,8 @@ internal class JsonModelForCombinedInterface : IUtf8JsonSerializable, IModelSeri { private Dictionary RawData { get; set; } = new Dictionary(); + public JsonModelForCombinedInterface() { } + /// Initializes a new instance of ModelXml for testing. /// /// @@ -43,7 +45,15 @@ internal JsonModelForCombinedInterface(string key, string value, string readOnly public string Value { get; set; } public string ReadOnlyProperty { get; } - void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + BinaryData data = ((IModelSerializable)this).Serialize(new ModelSerializerOptions()); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } internal static JsonModelForCombinedInterface DeserializeJsonModelForCombinedInterface(JsonElement element, ModelSerializerOptions options) { @@ -78,23 +88,10 @@ internal static JsonModelForCombinedInterface DeserializeJsonModelForCombinedInt return new JsonModelForCombinedInterface(key, value, readOnlyProperty, rawData); } - Stream IModelSerializable.Serialize(ModelSerializerOptions options) - { - Stream s = new MemoryStream(); - Utf8JsonWriter writer = new Utf8JsonWriter(s); - SerializeWithWriter(writer, options); - writer.Flush(); - return s; - } - - internal static JsonModelForCombinedInterface DeserializeJsonModelForCombinedInterface(Stream s, ModelSerializerOptions options) - { - var json = JsonDocument.Parse(s); - return DeserializeJsonModelForCombinedInterface(json.RootElement, options); - } - - private void SerializeWithWriter(Utf8JsonWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { + MemoryStream stream = new MemoryStream(); + Utf8JsonWriter writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); writer.WritePropertyName("key"u8); writer.WriteStringValue(Key); @@ -119,6 +116,14 @@ private void SerializeWithWriter(Utf8JsonWriter writer, ModelSerializerOptions o } } writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeJsonModelForCombinedInterface(JsonDocument.Parse(data.ToString()).RootElement, options); } } } 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 4f9d01842967..38ec27916103 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs @@ -8,12 +8,16 @@ using NUnit.Framework; using System.Xml.Serialization; using System.Collections.Generic; +using System.IO; +using System.Text.Json; namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { [XmlRoot("Tag")] - internal class ModelXml : IXmlSerializable, IXmlSerializableModel + internal class ModelXml : IXmlSerializable, IModelSerializable { + internal ModelXml() { } + /// Initializes a new instance of ModelXml for testing. /// /// @@ -38,7 +42,25 @@ public ModelXml(string key, string value, string readonlyProperty) [XmlElement("ReadOnlyProperty")] public string ReadOnlyProperty { get; } - void IXmlSerializable.Write(XmlWriter writer, string nameHint) => ((IXmlSerializableModel)this).Serialize(writer, new ModelSerializerOptions() {NameHint = nameHint}); + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => Serialize(writer, new ModelSerializerOptions() {NameHint = nameHint}); + + private void Serialize(XmlWriter writer, ModelSerializerOptions options) + { + writer.WriteStartElement(options.NameHint ?? "Tag"); + writer.WriteStartElement("Key"); + writer.WriteValue(Key); + writer.WriteEndElement(); + writer.WriteStartElement("Value"); + writer.WriteValue(Value); + writer.WriteEndElement(); + if (!options.IgnoreReadOnlyProperties) + { + writer.WriteStartElement("ReadOnlyProperty"); + writer.WriteValue(ReadOnlyProperty); + writer.WriteEndElement(); + } + writer.WriteEndElement(); + } internal static ModelXml DeserializeModelXml(XElement element, ModelSerializerOptions options = default) { @@ -60,22 +82,19 @@ internal static ModelXml DeserializeModelXml(XElement element, ModelSerializerOp return new ModelXml(key, value, readonlyProperty); } - void IXmlSerializableModel.Serialize(XmlWriter writer, ModelSerializerOptions options) + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) { - writer.WriteStartElement(options.NameHint ?? "Tag"); - writer.WriteStartElement("Key"); - writer.WriteValue(Key); - writer.WriteEndElement(); - writer.WriteStartElement("Value"); - writer.WriteValue(Value); - writer.WriteEndElement(); - if (!options.IgnoreReadOnlyProperties) - { - writer.WriteStartElement("ReadOnlyProperty"); - writer.WriteValue(ReadOnlyProperty); - writer.WriteEndElement(); - } - writer.WriteEndElement(); + MemoryStream stream = new MemoryStream(); + XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeModelXml(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 6575c993df3d..7dc12cb128bc 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs @@ -8,12 +8,15 @@ using NUnit.Framework; using System.Xml.Serialization; using System.IO; +using System.Text.Json; namespace Azure.Core.Tests.Public.ModelSerializationTests.Models { [XmlRoot("Tag")] internal class XmlModelForCombinedInterface : IXmlSerializable, IModelSerializable { + public XmlModelForCombinedInterface() { } + /// Initializes a new instance of ModelXml for testing. /// /// @@ -38,10 +41,7 @@ public XmlModelForCombinedInterface(string key, string value, string readOnlyPro [XmlElement("ReadOnlyProperty")] public string ReadOnlyProperty { get; } - void IXmlSerializable.Write(XmlWriter writer, string nameHint) => ((IModelSerializable)this).Serialize(new ModelSerializerOptions() { NameHint = nameHint }); - - internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInterface(Stream stream, ModelSerializerOptions options = default) - => DeserializeXmlModelForCombinedInterface(XElement.Load(stream), options); + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => Serialize(writer, new ModelSerializerOptions() { NameHint = nameHint }); internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInterface(XElement element, ModelSerializerOptions options = default) { @@ -63,16 +63,7 @@ internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInter return new XmlModelForCombinedInterface(key, value, readOnlyProperty); } - Stream IModelSerializable.Serialize(ModelSerializerOptions options) - { - Stream stream = new MemoryStream(); - XmlWriter writer = XmlWriter.Create(stream); - SerializeWithWriter(writer, options); - writer.Flush(); - return stream; - } - - private void SerializeWithWriter(XmlWriter writer, ModelSerializerOptions options) + private void Serialize(XmlWriter writer, ModelSerializerOptions options) { writer.WriteStartElement(options.NameHint ?? "Tag"); writer.WriteStartElement("Key"); @@ -89,5 +80,17 @@ private void SerializeWithWriter(XmlWriter writer, ModelSerializerOptions option } writer.WriteEndElement(); } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + MemoryStream stream = new MemoryStream(); + XmlWriter writer = XmlWriter.Create(stream); + Serialize(writer, options); + writer.Flush(); + stream.Position = 0; + return new BinaryData(stream.ToArray()); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) => DeserializeXmlModelForCombinedInterface(XElement.Load(data.ToStream()), options); } } diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/NewtonSoftTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/NewtonSoftTests.cs index 6d81b032c8f6..fda65f1821bd 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/NewtonSoftTests.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/NewtonSoftTests.cs @@ -50,7 +50,7 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) else options.Serializers.Add(typeof(Animal), new NewtonsoftJsonObjectSerializer()); - var model = ModelSerializer.DeserializeJson(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); + var model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); if (!ignoreReadOnly) { @@ -60,15 +60,14 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) Assert.IsFalse(model.IsHungry); Assert.That(model.Weight, Is.EqualTo(2.5)); - stream = ModelSerializer.SerializeJson(model, options); - stream.Position = 0; - string roundTrip = new StreamReader(stream).ReadToEnd(); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); #if NET6_0_OR_GREATER Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); #endif - var model2 = ModelSerializer.DeserializeJson(new MemoryStream(Encoding.UTF8.GetBytes(roundTrip)), options: options); + var model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options: options); VerifyModels.CheckAnimals(model, model2, options); } diff --git a/sdk/core/Azure.Core/tests/public/Samples/SerializationSamples.cs b/sdk/core/Azure.Core/tests/public/Samples/SerializationSamples.cs index 229faa59e282..9cdd6e3aa2e5 100644 --- a/sdk/core/Azure.Core/tests/public/Samples/SerializationSamples.cs +++ b/sdk/core/Azure.Core/tests/public/Samples/SerializationSamples.cs @@ -41,36 +41,6 @@ public void ExplicitCastDeserialize() #endregion } - [Test] - [Ignore("Only verifying that the sample builds")] - public void StjSerialize() - { - #region Snippet:Stj_Serialize - DogListProperty dog = new DogListProperty - { - Name = "Doggo", - IsHungry = false, - Weight = 1.1, - FoodConsumed = { "kibble", "egg", "peanut butter" }, - }; - - //STJ example - string json = System.Text.Json.JsonSerializer.Serialize(dog); - #endregion - } - - [Test] - [Ignore("Only verifying that the sample builds")] - public void StjDeserialize() - { - #region Snippet:Stj_Deserialize - string json = "{\"latinName\":\"Animalia\",\"weight\":1.1,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; - - //stj example - DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json); - #endregion - } - [Test] [Ignore("Only verifying that the sample builds")] public void NewtonSoftSerialize() @@ -86,7 +56,7 @@ public void NewtonSoftSerialize() ModelSerializerOptions options = new ModelSerializerOptions(); options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); - Stream stream = ModelSerializer.SerializeJson(dog, options); + BinaryData data = ModelSerializer.Serialize(dog, options); #endregion } @@ -99,35 +69,7 @@ public void NewtonSoftDeserialize() options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; - DogListProperty dog = ModelSerializer.DeserializeJson(json, options); - #endregion - } - - [Test] - [Ignore("Only verifying that the sample builds")] - public void ModelSerializerSerialize() - { - #region Snippet:ModelSerializer_Serialize - DogListProperty dog = new DogListProperty - { - Name = "Doggo", - IsHungry = true, - Weight = 1.1, - FoodConsumed = { "kibble", "egg", "peanut butter" }, - }; - - Stream stream = ModelSerializer.SerializeJson(dog); - #endregion - } - - [Test] - [Ignore("Only verifying that the sample builds")] - public void ModelSerializerDeserialize() - { - #region Snippet:ModelSerializer_Deserialize - string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; - - DogListProperty dog = ModelSerializer.DeserializeJson(json); + DogListProperty dog = ModelSerializer.Deserialize(BinaryData.FromString(json), options); #endregion } @@ -175,7 +117,7 @@ public void BYOMWithNewtonsoftSerialize() envelope.ModelT = new ModelT { Name = "Fluffy", Age = 10 }; ModelSerializerOptions options = new ModelSerializerOptions(); options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); - Stream stream = ModelSerializer.SerializeJson(envelope, options); + BinaryData data = ModelSerializer.Serialize(envelope, options); #endregion } @@ -193,34 +135,7 @@ public void BYOMWithNewtonsoftDeserialize() ModelSerializerOptions options = new ModelSerializerOptions(); options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); - Envelope model = ModelSerializer.DeserializeJson>(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); - #endregion - } - - [Test] - [Ignore("Only verifying that the sample builds")] - public void XmlModelSerialize() - { - #region Snippet:XmlModelSerialize - ModelXml modelXml = new ModelXml("Color", "Red", "ReadOnly"); - var stream = ModelSerializer.SerializeXml(modelXml); - stream.Position = 0; - string roundTrip = new StreamReader(stream).ReadToEnd(); - #endregion - } - - [Test] - [Ignore("Only verifying that the sample builds")] - public void XmlModelDeserialize() - { - #region Snippet:XmlModelDeserialize - string serviceResponse = - "" + - "Color" + - "Red" + - ""; - - ModelXml model = ModelSerializer.DeserializeXml(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse))); + Envelope model = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); #endregion } @@ -230,14 +145,12 @@ public void IModelSerializableSerialize() { #region Snippet:ModelSerializer_IModelSerializable_Serialize XmlModelForCombinedInterface xmlModel = new XmlModelForCombinedInterface("Color", "Red", "ReadOnly"); - var stream = ModelSerializer.Serialize(xmlModel); - stream.Position = 0; - string xmlString = new StreamReader(stream).ReadToEnd(); + var data = ModelSerializer.Serialize(xmlModel); + string xmlString = data.ToString(); JsonModelForCombinedInterface jsonModel = new JsonModelForCombinedInterface("Color", "Red", "ReadOnly"); - stream = ModelSerializer.Serialize(jsonModel); - stream.Position = 0; - string jsonString = new StreamReader(stream).ReadToEnd(); + data = ModelSerializer.Serialize(jsonModel); + string jsonString = data.ToString(); #endregion } @@ -247,10 +160,10 @@ public void IModelSerializableDeserialize() { #region Snippet:ModelSerializer_IModelSerializable_Deserialize string xmlResponse = "ColorRed"; - XmlModelForCombinedInterface xmlModel = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(xmlResponse))); + XmlModelForCombinedInterface xmlModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(xmlResponse))); string jsonResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"x\":\"extra\"}"; - JsonModelForCombinedInterface jsonModel = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(jsonResponse))); + JsonModelForCombinedInterface jsonModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(jsonResponse))); #endregion }