diff --git a/sdk/core/Azure.Core/api/Azure.Core.net461.cs b/sdk/core/Azure.Core/api/Azure.Core.net461.cs index 712129f69a7b..1445754f6199 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net461.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net461.cs @@ -1173,7 +1173,7 @@ public SerializableOptions() { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public bool IgnoreReadOnlyProperties { get { throw null; } set { } } public bool PrettyPrint { get { throw null; } set { } } - public Azure.Core.Serialization.ObjectSerializer? Serializer { get { throw null; } set { } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } } } namespace Azure.Messaging 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 41eea257a267..0d2314557ad7 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs @@ -1173,7 +1173,7 @@ public SerializableOptions() { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public bool IgnoreReadOnlyProperties { get { throw null; } set { } } public bool PrettyPrint { get { throw null; } set { } } - public Azure.Core.Serialization.ObjectSerializer? Serializer { get { throw null; } set { } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } } } namespace Azure.Messaging 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 41eea257a267..0d2314557ad7 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs @@ -1173,7 +1173,7 @@ public SerializableOptions() { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public bool IgnoreReadOnlyProperties { get { throw null; } set { } } public bool PrettyPrint { get { throw null; } set { } } - public Azure.Core.Serialization.ObjectSerializer? Serializer { get { throw null; } set { } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } } } namespace Azure.Messaging 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 712129f69a7b..1445754f6199 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs @@ -1173,7 +1173,7 @@ public SerializableOptions() { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public bool IgnoreReadOnlyProperties { get { throw null; } set { } } public bool PrettyPrint { get { throw null; } set { } } - public Azure.Core.Serialization.ObjectSerializer? Serializer { get { throw null; } set { } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } } } namespace Azure.Messaging 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 712129f69a7b..1445754f6199 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs @@ -1173,7 +1173,7 @@ public SerializableOptions() { } public bool IgnoreAdditionalProperties { get { throw null; } set { } } public bool IgnoreReadOnlyProperties { get { throw null; } set { } } public bool PrettyPrint { get { throw null; } set { } } - public Azure.Core.Serialization.ObjectSerializer? Serializer { get { throw null; } set { } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } } } namespace Azure.Messaging diff --git a/sdk/core/Azure.Core/samples/Serialization.md b/sdk/core/Azure.Core/samples/Serialization.md index 274d08383efc..07890959ef14 100644 --- a/sdk/core/Azure.Core/samples/Serialization.md +++ b/sdk/core/Azure.Core/samples/Serialization.md @@ -100,7 +100,7 @@ DogListProperty dog = new DogListProperty }; //STJ example -string json = JsonSerializer.Serialize(dog); +string json = System.Text.Json.JsonSerializer.Serialize(dog); ``` Deserialization @@ -109,7 +109,7 @@ Deserialization string json = "{\"latinName\":\"Animalia\",\"weight\":1.1,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; //stj example -DogListProperty dog = JsonSerializer.Deserialize(json); +DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json); ``` ## Using static deserializer @@ -162,7 +162,7 @@ DogListProperty dog = new DogListProperty FoodConsumed = { "kibble", "egg", "peanut butter" }, }; SerializableOptions options = new SerializableOptions(); -options.Serializer = new NewtonsoftJsonObjectSerializer(); +options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); Stream stream = ModelSerializer.Serialize(dog, options); ``` @@ -171,7 +171,7 @@ Deserialization ```C# Snippet:NewtonSoft_Deserialize SerializableOptions options = new SerializableOptions(); -options.Serializer = new NewtonsoftJsonObjectSerializer(); +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.Deserialize(json, options); @@ -195,7 +195,7 @@ DogListProperty dog = new DogListProperty JsonSerializerOptions options = new JsonSerializerOptions(); options.Converters.Add(new ModelJsonConverter(false)); -string json = JsonSerializer.Serialize(dog, options); +string json = System.Text.Json.JsonSerializer.Serialize(dog, options); ``` Deserialization @@ -206,5 +206,41 @@ string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo""," JsonSerializerOptions options = new JsonSerializerOptions(); options.Converters.Add(new ModelJsonConverter(false)); -DogListProperty dog = JsonSerializer.Deserialize(json, options); +DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json, options); +``` + +## Envelope BYOM Case +The following examples show a use case where a User brings a model unknown to the Serializer. The serialization used for each model can also be set in the SerializableOptions options property Serializers. + +Model Being Used by User +private class ModelT + { + public string Name { get; set; } + public int Age { get; set; } + } + +Serialization +```C# Snippet:BYOMWithNewtonsoftSerialize +Envelope envelope = new Envelope(); +envelope.ModelA = new CatReadOnlyProperty(); +envelope.ModelT = new ModelT { Name = "Fluffy", Age = 10 }; + +SerializableOptions options = new SerializableOptions(); +options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); + +Stream stream = ModelSerializer.Serialize(envelope, options); +``` + +Deserialization +```C# Snippet:BYOMWithNewtonsoftDeserialize +string serviceResponse = + "{\"readOnlyProperty\":\"read\"," + + "\"modelA\":{\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5}," + + "\"modelT\":{\"Name\":\"hello\",\"Age\":1}" + + "}"; + +SerializableOptions options = new SerializableOptions(); +options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); + +Envelope model = ModelSerializer.Deserialize>(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); ``` diff --git a/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs index 391d64a566fe..701d16c87ecf 100644 --- a/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs +++ b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs @@ -20,13 +20,18 @@ public static class ModelSerializer /// public static Stream Serialize(T model, SerializableOptions? options = default) where T : IJsonSerializable, new() { - // if options.Serializer is set - if (options != null && options.Serializer != null) + // if options.Serializers is set and the model is in the dictionary, use the serializer + if (options != null) { - System.BinaryData data = options.Serializer.Serialize(model); - return data.ToStream(); - } + ObjectSerializer? serializer; + if (options.Serializers.TryGetValue(typeof(T), out serializer)) + { + System.BinaryData data = serializer.Serialize(model); + return data.ToStream(); + } + } + // else use default STJ serializer IJsonSerializable serializable = (model ??= new T()) as IJsonSerializable; Stream stream = new MemoryStream(); serializable.Serialize(stream, options); @@ -42,13 +47,18 @@ public static class ModelSerializer /// public static T Deserialize(Stream stream, SerializableOptions? options = default) where T : IJsonSerializable, new() { - if (options != null && options.Serializer != null) + if (options != null) { - var obj = options.Serializer.Deserialize(stream, typeof(T), default); - if (obj is null) - throw new InvalidOperationException(); - else - return (T)obj; + ObjectSerializer? serializer; + + if (options.Serializers.TryGetValue(typeof(T), out serializer)) + { + var obj = serializer.Deserialize(stream, typeof(T), default); //problem here T is Envelope and typeof(T) is Envelope but we want typeof(T) to be DogListProperty + if (obj is null) + throw new InvalidOperationException(); + else + return (T)obj; + } } IJsonSerializable serializable = new T(); @@ -69,14 +79,18 @@ public static class ModelSerializer using StreamWriter writer = new StreamWriter(stream); writer.Write(json); stream.Position = 0; + ObjectSerializer? serializer; - if (options != null && options.Serializer != null) + if (options != null) { - var obj = options.Serializer.Deserialize(stream, typeof(T), default); - if (obj is null) - throw new InvalidOperationException(); - else - return (T)obj; + 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; + } } IJsonSerializable serializable = new T(); diff --git a/sdk/core/Azure.Core/src/Serialization/SerializableOptions.cs b/sdk/core/Azure.Core/src/Serialization/SerializableOptions.cs index a9c201ced116..3333b7acab5b 100644 --- a/sdk/core/Azure.Core/src/Serialization/SerializableOptions.cs +++ b/sdk/core/Azure.Core/src/Serialization/SerializableOptions.cs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; +using System.Collections.Generic; + namespace Azure.Core.Serialization { /// @@ -24,8 +27,8 @@ public class SerializableOptions public bool PrettyPrint { get; set; } /// - /// todo + /// Dictionary that holds all the serializers for the different model types. /// - public ObjectSerializer? Serializer { get; set; } + public Dictionary Serializers { get; } = new Dictionary(); } } diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/Envelope.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/Envelope.cs new file mode 100644 index 000000000000..bf2a866335b3 --- /dev/null +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/Envelope.cs @@ -0,0 +1,193 @@ +// 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 Azure.Core.Serialization; + +namespace Azure.Core.Tests.ModelSerializationTests +{ + public class Envelope : IJsonSerializable, IUtf8JsonSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public string ReadOnlyProperty { get; private set; } = "readonly"; + + public Envelope() + { + } + + public Envelope(string location) + { + ReadOnlyProperty = location; + } + + internal Envelope(string property, CatReadOnlyProperty cat, T modelC, Dictionary rawData) + { + ReadOnlyProperty = property; + ModelA = cat; + ModelT = modelC; + RawData = rawData; + } + + public CatReadOnlyProperty ModelA { get; set; } + public T ModelT { get; set; } + + #region Serialization + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer, SerializableOptions options) + { + writer.WriteStartObject(); + if (!options.IgnoreReadOnlyProperties) + { + writer.WritePropertyName("readOnlyProperty"u8); + writer.WriteStringValue(ReadOnlyProperty); + } + + writer.WritePropertyName("modelA"u8); + ((IUtf8JsonSerializable)ModelA).Write(writer, options); + writer.WritePropertyName("modelC"u8); + SerializeT(writer, options); + + if (!options.IgnoreAdditionalProperties) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static Envelope DeserializeEnvelope(JsonElement element, SerializableOptions options) + { + string readonlyProperty = ""; + CatReadOnlyProperty modelA = new CatReadOnlyProperty(); + T modelC = default(T); + Dictionary rawData = new Dictionary(); + + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("readOnlyProperty"u8)) + { + readonlyProperty = property.Value.GetString(); + continue; + } + if (property.NameEquals("modelA"u8)) + { + modelA = CatReadOnlyProperty.DeserializeCatReadOnlyProperty(property.Value, options); + continue; + } + if (property.NameEquals("modelC"u8)) + { + modelC = DeserializeT(property.Value, options); + continue; + } + + if (!options.IgnoreAdditionalProperties) + { + //this means it's an modelC property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new Envelope(readonlyProperty, modelA, modelC, rawData); + } + + private void SerializeT(Utf8JsonWriter writer, SerializableOptions options) + { + ObjectSerializer serializer = GetObjectSerializer(options); + BinaryData data = serializer.Serialize(ModelT); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } + + private static ObjectSerializer GetObjectSerializer(SerializableOptions options) + { + ObjectSerializer serializer; + if (options.Serializers.TryGetValue(typeof(T), out serializer)) + { + // serializer is from the dictionary + return serializer; + } + // default + return JsonObjectSerializer.Default; + } + + private static T DeserializeT(JsonElement element, SerializableOptions options) + { + ObjectSerializer serializer = GetObjectSerializer(options); + MemoryStream m = new MemoryStream(); + Utf8JsonWriter w = new Utf8JsonWriter(m); + element.WriteTo(w); + w.Flush(); + m.Position = 0; + return (T)serializer.Deserialize(m, typeof(T), default); + } +#endregion + + #region InterfaceImplementation + public bool TryDeserialize(Stream stream, out long bytesConsumed, SerializableOptions options = default) + { + bytesConsumed = 0; + try + { + Deserialize(stream, options); + bytesConsumed = stream.Length; + return true; + } + catch + { + return false; + } + } + + public bool TrySerialize(Stream stream, out long bytesWritten, SerializableOptions options = default) + { + bytesWritten = 0; + try + { + Serialize(stream, options); + bytesWritten = stream.Length; + return true; + } + catch + { + return false; + } + } + + public void Deserialize(Stream stream, SerializableOptions options = default) + { + JsonDocument jsonDocument = JsonDocument.Parse(stream); + var model = DeserializeEnvelope(jsonDocument.RootElement, options ?? new SerializableOptions()); + this.ReadOnlyProperty = model.ReadOnlyProperty; + this.ModelA = model.ModelA; + this.ModelT = model.ModelT; + this.RawData = model.RawData; + } + + public void Serialize(Stream stream, SerializableOptions options = default) + { + JsonWriterOptions jsonWriterOptions = new JsonWriterOptions(); + if (options.PrettyPrint) + { + jsonWriterOptions.Indented = true; + } + Utf8JsonWriter writer = new Utf8JsonWriter(stream, jsonWriterOptions); + ((IUtf8JsonSerializable)this).Write(writer, options ?? new SerializableOptions()); + writer.Flush(); + } + #endregion + } +} diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/EnvelopeTests.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/EnvelopeTests.cs new file mode 100644 index 000000000000..3cbb6b81228d --- /dev/null +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/EnvelopeTests.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using Azure.Core.Serialization; +using NUnit.Framework; +using Newtonsoft.Json; + +namespace Azure.Core.Tests.ModelSerializationTests +{ + internal class EnvelopeTests + { + private readonly SerializableOptions _wireOptions = new SerializableOptions { IgnoreReadOnlyProperties = false }; + private readonly SerializableOptions _objectOptions = new SerializableOptions(); + + [TestCase(true)] + [TestCase(false)] + public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) + { + Stream stream = new MemoryStream(); + string serviceResponse = + "{\"readOnlyProperty\":\"read\"," + + "\"modelA\":{\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5}," + + "\"modelC\":{\"x\":\"hello\",\"y\":\"bye\"}" + + "}"; + + StringBuilder expectedSerialized = new StringBuilder("{"); + if (!ignoreReadOnly) + { + expectedSerialized.Append("\"readOnlyProperty\":\"read\","); + } + expectedSerialized.Append("\"modelA\":{"); + if (!ignoreReadOnly) + { + expectedSerialized.Append("\"latinName\":\"Felis catus\",\"hasWhiskers\":false,"); + } + expectedSerialized.Append("\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5},"); + expectedSerialized.Append("\"modelC\":{\"X\":\"hello\",\"Y\":\"bye\"}"); //using NewtonSoft Serializer + expectedSerialized.Append("}"); + var expectedSerializedString = expectedSerialized.ToString(); + + SerializableOptions options = new SerializableOptions() { IgnoreReadOnlyProperties = ignoreReadOnly }; + + if (ignoreReadOnly) + { + JsonSerializerSettings settings = new JsonSerializerSettings + { + ContractResolver = new IgnoreReadOnlyPropertiesResolver() + }; + options.Serializers.Add(typeof(ModelC), new NewtonsoftJsonObjectSerializer(settings)); + } + else + options.Serializers.Add(typeof(ModelC), new NewtonsoftJsonObjectSerializer()); + + Envelope m = new Envelope(); + m.Deserialize( + new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), + options: options); + Envelope model = ModelSerializer.Deserialize>(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); + + if (!ignoreReadOnly) + { + Assert.That(model.ReadOnlyProperty, Is.EqualTo("read")); + } + + CatReadOnlyProperty correctCat = new CatReadOnlyProperty(2.5, default, "Cat", false, default); + VerifyModels.CheckAnimals(correctCat, model.ModelA, options); + Assert.AreEqual("hello", model.ModelT.X); + Assert.AreEqual("bye", model.ModelT.Y); + stream = ModelSerializer.Serialize(model, options); + stream.Position = 0; + string roundTrip = new StreamReader(stream).ReadToEnd(); + + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + + var model2 = ModelSerializer.Deserialize>(new MemoryStream(Encoding.UTF8.GetBytes(roundTrip)), options: options); + ModelC correctModelC = new ModelC("hello", "bye"); + ModelC.VerifyModelC(correctModelC, model2.ModelT); + } + + // Generate a class that implements the NewtonSoft default contract resolver so that ReadOnly properties are not serialized + // This is used to verify that the ReadOnly properties are not serialized when IgnoreReadOnlyProperties is set to true + private class IgnoreReadOnlyPropertiesResolver : Newtonsoft.Json.Serialization.DefaultContractResolver + { + protected override Newtonsoft.Json.Serialization.JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization) + { + Newtonsoft.Json.Serialization.JsonProperty property = base.CreateProperty(member, memberSerialization); + + if (!property.Writable) + { + property.ShouldSerialize = obj => false; + } + + return property; + } + } + + private class ModelC + { + public ModelC(string x1, string y1) + { + X = x1; + Y = y1; + } + + public string X { get; set; } + public string Y { get; set; } + + public static void VerifyModelC(ModelC c1, ModelC c2) + { + Assert.That(c1.X == c2.X); + Assert.That(c1.Y == c2.Y); + } + } + } +} diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/CatReadOnlyProperty.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/CatReadOnlyProperty.cs index aa949de67c6e..bdd80f985afc 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/CatReadOnlyProperty.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/CatReadOnlyProperty.cs @@ -23,6 +23,10 @@ internal CatReadOnlyProperty(double weight, string latinName, string name, bool RawData = rawData; } + internal CatReadOnlyProperty() + { + } + public bool HasWhiskers { get; private set; } = true; #region Serialization diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/NewtonSoftTests.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/NewtonSoftTests.cs index 957080bf0095..352d35c1bb48 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/NewtonSoftTests.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/NewtonSoftTests.cs @@ -24,11 +24,11 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) { Stream stream = new MemoryStream(); string serviceResponse = - "{\"latinName\":\"Animalia\",\"weight\":2.3,\"name\":\"Rabbit\",\"isHungry\":false}"; + "{\"latinName\":\"Animalia\",\"weight\":2.5,\"name\":\"Rabbit\",\"isHungry\":false}"; StringBuilder expectedSerialized = new StringBuilder("{"); expectedSerialized.Append("\"IsHungry\":false,"); - expectedSerialized.Append("\"Weight\":2.3,"); + expectedSerialized.Append("\"Weight\":2.5,"); if (!ignoreReadOnly) { expectedSerialized.Append("\"LatinName\":\"Animalia\","); @@ -45,10 +45,10 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) { ContractResolver = new IgnoreReadOnlyPropertiesResolver() }; - options.Serializer = new NewtonsoftJsonObjectSerializer(settings); + options.Serializers.Add(typeof(Animal), new NewtonsoftJsonObjectSerializer(settings)); } else - options.Serializer = new NewtonsoftJsonObjectSerializer(); + options.Serializers.Add(typeof(Animal), new NewtonsoftJsonObjectSerializer()); var model = ModelSerializer.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); @@ -58,10 +58,7 @@ public void CanRoundTripFutureVersionWithoutLoss(bool ignoreReadOnly) } Assert.That(model.Name, Is.EqualTo("Rabbit")); Assert.IsFalse(model.IsHungry); - -#if NET6_0_OR_GREATER - Assert.That(model.Weight, Is.EqualTo(2.3)); -#endif + Assert.That(model.Weight, Is.EqualTo(2.5)); stream = ModelSerializer.Serialize(model, options); stream.Position = 0; diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/ReadOnlyPropertyTests.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/ReadOnlyPropertyTests.cs index bf6bea5f6fb1..77398cb61cfc 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/ReadOnlyPropertyTests.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/ReadOnlyPropertyTests.cs @@ -139,7 +139,7 @@ public void CanRoundTripNoTry(bool ignoreReadOnly, bool ignoreUnknown) public void PrettyPrint() { #if NET6_0_OR_GREATER - CatReadOnlyProperty model = new CatReadOnlyProperty(3.2, "Felis catus", "Catto", true, false); + CatReadOnlyProperty model = new CatReadOnlyProperty(2.5, "Felis catus", "Catto", true, false); Stream stream = new MemoryStream(); model.TrySerialize(stream, out long bytesWritten, options: new SerializableOptions() { PrettyPrint = true });; @@ -152,7 +152,7 @@ public void PrettyPrint() "hasWhiskers": false, "name": "Catto", "isHungry": true, - "weight": 3.2 + "weight": 2.5 } """; diff --git a/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs b/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs index 4ff0bf95e14e..fef743106229 100644 --- a/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs +++ b/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs @@ -9,6 +9,7 @@ using Azure.Core.Serialization; using Azure.Core.TestFramework; using Azure.Core.Tests.ModelSerializationTests; +using Newtonsoft.Json; using NUnit.Framework; namespace Azure.Core.Samples @@ -112,7 +113,7 @@ public void StjSerialize() }; //STJ example - string json = JsonSerializer.Serialize(dog); + string json = System.Text.Json.JsonSerializer.Serialize(dog); #endregion } @@ -124,7 +125,7 @@ public void StjDeserialize() string json = "{\"latinName\":\"Animalia\",\"weight\":1.1,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; //stj example - DogListProperty dog = JsonSerializer.Deserialize(json); + DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json); #endregion } @@ -154,7 +155,7 @@ public void NewtonSoftSerialize() FoodConsumed = { "kibble", "egg", "peanut butter" }, }; SerializableOptions options = new SerializableOptions(); - options.Serializer = new NewtonsoftJsonObjectSerializer(); + options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); Stream stream = ModelSerializer.Serialize(dog, options); #endregion @@ -166,7 +167,7 @@ public void NewtonSoftDeserialize() { #region Snippet:NewtonSoft_Deserialize SerializableOptions options = new SerializableOptions(); - options.Serializer = new NewtonsoftJsonObjectSerializer(); + 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.Deserialize(json, options); @@ -217,7 +218,7 @@ public void ModelConverterSerialize() JsonSerializerOptions options = new JsonSerializerOptions(); options.Converters.Add(new ModelJsonConverter(false)); - string json = JsonSerializer.Serialize(dog, options); + string json = System.Text.Json.JsonSerializer.Serialize(dog, options); #endregion } @@ -231,8 +232,48 @@ public void ModelConverterDeserialize() JsonSerializerOptions options = new JsonSerializerOptions(); options.Converters.Add(new ModelJsonConverter(false)); - DogListProperty dog = JsonSerializer.Deserialize(json, options); + DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json, options); #endregion } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void BYOMWithNewtonsoftSerialize() + { + #region Snippet:BYOMWithNewtonsoftSerialize + Envelope envelope = new Envelope(); + envelope.ModelA = new CatReadOnlyProperty(); + envelope.ModelT = new ModelT { Name = "Fluffy", Age = 10 }; + + SerializableOptions options = new SerializableOptions(); + options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); + + Stream stream = ModelSerializer.Serialize(envelope, options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void BYOMWithNewtonsoftDeserialize() + { + #region Snippet:BYOMWithNewtonsoftDeserialize + string serviceResponse = + "{\"readOnlyProperty\":\"read\"," + + "\"modelA\":{\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5}," + + "\"modelT\":{\"Name\":\"hello\",\"Age\":1}" + + "}"; + + SerializableOptions options = new SerializableOptions(); + options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); + + Envelope model = ModelSerializer.Deserialize>(new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)), options: options); + #endregion + } + + private class ModelT + { + public string Name { get; set; } + public int Age { get; set; } + } } }