diff --git a/sdk/core/Azure.Core/api/Azure.Core.net461.cs b/sdk/core/Azure.Core/api/Azure.Core.net461.cs index fda9495c23be..544ef29668ed 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net461.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net461.cs @@ -1088,6 +1088,15 @@ public override void Serialize(System.IO.Stream stream, object? value, System.Ty public override System.Threading.Tasks.ValueTask SerializeAsync(System.IO.Stream stream, object? value, System.Type inputType, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask SerializeAsync(object? value, System.Type? inputType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(bool ignoreAdditionalProperties) { } + public bool IgnoreAdditionalProperties { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonSerializable 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.IJsonSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } public static partial class ModelSerializer { public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.SerializableOptions? options = null) where T : Azure.Core.Serialization.IJsonSerializable, new() { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs index 495c8e16746f..bb02b2ad7801 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs @@ -1088,6 +1088,15 @@ public override void Serialize(System.IO.Stream stream, object? value, System.Ty public override System.Threading.Tasks.ValueTask SerializeAsync(System.IO.Stream stream, object? value, System.Type inputType, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask SerializeAsync(object? value, System.Type? inputType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(bool ignoreAdditionalProperties) { } + public bool IgnoreAdditionalProperties { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonSerializable 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.IJsonSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } public static partial class ModelSerializer { public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.SerializableOptions? options = null) where T : Azure.Core.Serialization.IJsonSerializable, new() { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs index 495c8e16746f..bb02b2ad7801 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs @@ -1088,6 +1088,15 @@ public override void Serialize(System.IO.Stream stream, object? value, System.Ty public override System.Threading.Tasks.ValueTask SerializeAsync(System.IO.Stream stream, object? value, System.Type inputType, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask SerializeAsync(object? value, System.Type? inputType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(bool ignoreAdditionalProperties) { } + public bool IgnoreAdditionalProperties { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonSerializable 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.IJsonSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } public static partial class ModelSerializer { public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.SerializableOptions? options = null) where T : Azure.Core.Serialization.IJsonSerializable, new() { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs index fda9495c23be..544ef29668ed 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs @@ -1088,6 +1088,15 @@ public override void Serialize(System.IO.Stream stream, object? value, System.Ty public override System.Threading.Tasks.ValueTask SerializeAsync(System.IO.Stream stream, object? value, System.Type inputType, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask SerializeAsync(object? value, System.Type? inputType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(bool ignoreAdditionalProperties) { } + public bool IgnoreAdditionalProperties { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonSerializable 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.IJsonSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } public static partial class ModelSerializer { public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.SerializableOptions? options = null) where T : Azure.Core.Serialization.IJsonSerializable, new() { throw null; } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs index fda9495c23be..544ef29668ed 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs @@ -1088,6 +1088,15 @@ public override void Serialize(System.IO.Stream stream, object? value, System.Ty public override System.Threading.Tasks.ValueTask SerializeAsync(System.IO.Stream stream, object? value, System.Type inputType, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask SerializeAsync(object? value, System.Type? inputType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(bool ignoreAdditionalProperties) { } + public bool IgnoreAdditionalProperties { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonSerializable 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.IJsonSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } public static partial class ModelSerializer { public static T Deserialize(System.IO.Stream stream, Azure.Core.Serialization.SerializableOptions? options = null) where T : Azure.Core.Serialization.IJsonSerializable, new() { throw null; } diff --git a/sdk/core/Azure.Core/samples/Serialization.md b/sdk/core/Azure.Core/samples/Serialization.md index 5634dbc96681..274d08383efc 100644 --- a/sdk/core/Azure.Core/samples/Serialization.md +++ b/sdk/core/Azure.Core/samples/Serialization.md @@ -176,3 +176,35 @@ string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo""," DogListProperty dog = ModelSerializer.Deserialize(json, options); ``` + +## Using ModelJsonConverter for JsonSerializer +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`. + +Serialization +```C# Snippet:ModelConverter_Serialize +DogListProperty dog = new DogListProperty +{ + Name = "Doggo", + IsHungry = true, + Weight = 1.1, + FoodConsumed = { "kibble", "egg", "peanut butter" }, +}; + +JsonSerializerOptions options = new JsonSerializerOptions(); +options.Converters.Add(new ModelJsonConverter(false)); + +string json = JsonSerializer.Serialize(dog, options); +``` + +Deserialization + +```C# Snippet:ModelConverter_Deserialize +string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; + +JsonSerializerOptions options = new JsonSerializerOptions(); +options.Converters.Add(new ModelJsonConverter(false)); + +DogListProperty dog = JsonSerializer.Deserialize(json, options); +``` diff --git a/sdk/core/Azure.Core/src/Azure.Core.csproj b/sdk/core/Azure.Core/src/Azure.Core.csproj index f70addadf729..db6bbb058d4f 100644 --- a/sdk/core/Azure.Core/src/Azure.Core.csproj +++ b/sdk/core/Azure.Core/src/Azure.Core.csproj @@ -57,6 +57,7 @@ + diff --git a/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs b/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs new file mode 100644 index 000000000000..caac5c4b0428 --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Azure.Core.Serialization +{ + /// + /// . + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + public class ModelJsonConverter : JsonConverter +#pragma warning restore AZC0014 // Avoid using banned types in public API + { + /// + /// . + /// + public bool IgnoreAdditionalProperties { get; } + + /// + /// . + /// + public ModelJsonConverter() + : this(true) { } + + /// + /// . + /// + /// + public ModelJsonConverter(bool ignoreAdditionalProperties) + { + IgnoreAdditionalProperties = ignoreAdditionalProperties; + } + + /// + /// . + /// + /// + /// + public override bool CanConvert(Type typeToConvert) + { + return (typeToConvert.GetInterfaces().Any(i => i is IJsonSerializable)); + } + + /// + /// . + /// + /// + /// + /// + /// + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + public override IJsonSerializable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) +#pragma warning restore AZC0014 // Avoid using banned types in public API + { + SerializableOptions serializableOptions = ConvertOptions(options); + var model = Activator.CreateInstance(typeToConvert, true) as IModelInternalSerializable; + if (model is null) + throw new NotSupportedException($"{typeToConvert.Name} does not have a parameterless constructor"); + + model.Deserialize(ref reader, serializableOptions); + return (IJsonSerializable)model; + } + + /// + /// . + /// + /// + /// + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + public override void Write(Utf8JsonWriter writer, IJsonSerializable value, JsonSerializerOptions options) +#pragma warning restore AZC0014 // Avoid using banned types in public API + { + SerializableOptions serializableOptions = ConvertOptions(options); + ((IModelInternalSerializable)value).Serialize(writer, serializableOptions); + } + + private SerializableOptions ConvertOptions(JsonSerializerOptions options) + { + SerializableOptions serializableOptions = new SerializableOptions(); + serializableOptions.IgnoreAdditionalProperties = IgnoreAdditionalProperties; + serializableOptions.IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties; + return serializableOptions; + } + } +} diff --git a/sdk/core/Azure.Core/src/Shared/IModelInternalSerializable.cs b/sdk/core/Azure.Core/src/Shared/IModelInternalSerializable.cs new file mode 100644 index 000000000000..6c3b8c810305 --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/IModelInternalSerializable.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Text.Json; +using Azure.Core.Serialization; + +namespace Azure.Core +{ + internal interface IModelInternalSerializable + { + /// + /// TODO + /// + /// + /// + void Serialize(Utf8JsonWriter writer, SerializableOptions? options = default); + + /// + /// TODO + /// + /// + /// + void Deserialize(ref Utf8JsonReader reader, SerializableOptions? options = default); + } +} diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/ModelJsonConverterTests.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/ModelJsonConverterTests.cs new file mode 100644 index 000000000000..56258412e475 --- /dev/null +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/ModelJsonConverterTests.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Azure.Core.Serialization; +using NUnit.Framework; + +namespace Azure.Core.Tests.ModelSerializationTests +{ + public class ModelJsonConverterTests + { + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void SerializeTest(bool ignoreReadonlyProperties, bool ignoreAdditionalProperties) + { + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(ignoreAdditionalProperties)); + options.IgnoreReadOnlyProperties = ignoreReadonlyProperties; + + string expected = "{"; + if (!ignoreReadonlyProperties) + expected += "\"latinName\":\"Animalia\","; + expected += "\"name\":\"Doggo\",\"isHungry\":false,"; + expected += "\"weight\":1.5,"; + expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; + var dog = new DogListProperty + { + Name = "Doggo", + IsHungry = false, + Weight = 1.5, + FoodConsumed = { "kibble", "egg", "peanut butter" }, + }; + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual(expected, actual); + } + + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void DeserializeTest(bool ignoreReadonlyProperties, bool ignoreAdditionalProperties) + { + string serviceResponse = "{\"latinName\":\"Animalia\",\"weight\":1.5,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(ignoreAdditionalProperties)); + options.IgnoreReadOnlyProperties = ignoreReadonlyProperties; + + var dog = JsonSerializer.Deserialize(serviceResponse, options); + + Assert.AreEqual("Doggo", dog.Name); + Assert.AreEqual(false, dog.IsHungry); + Assert.AreEqual(1.5, dog.Weight); + CollectionAssert.AreEquivalent(new List { "kibble", "egg", "peanut butter" }, dog.FoodConsumed); + Assert.AreEqual("Animalia", dog.LatinName); + + var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.AreEqual(!ignoreAdditionalProperties, additionalProperties.ContainsKey("numberOfLegs")); + if (!ignoreAdditionalProperties) + Assert.AreEqual("4", additionalProperties["numberOfLegs"].ToString()); + + string expected = "{"; + if (!ignoreReadonlyProperties) + expected += "\"latinName\":\"Animalia\","; + expected += "\"name\":\"Doggo\",\"isHungry\":false,"; + expected += "\"weight\":1.5,"; + expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]"; + if (!ignoreAdditionalProperties) + expected += ",\"numberOfLegs\":4"; + expected += "}"; + + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual(expected, actual); + } + + [Test] + public void UsesMoreConcreteConverter() + { + string serviceResponse = "{\"latinName\":\"Animalia\",\"weight\":1.5,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter()); + options.Converters.Add(new DogListPropertyBlankConverter()); + + //the more concrete converter should be used so we will validate the default values are used + var dog = JsonSerializer.Deserialize(serviceResponse, options); + Assert.AreEqual("Animal", dog.Name); + Assert.AreEqual(false, dog.IsHungry); + Assert.AreEqual(1.1d, dog.Weight); + CollectionAssert.AreEquivalent(new List(), dog.FoodConsumed); + Assert.AreEqual("Animalia", dog.LatinName); + + var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.AreEqual(0, additionalProperties.Count); + + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual("", actual); + } + } +} diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/Animal.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/Animal.cs similarity index 90% rename from sdk/core/Azure.Core/tests/ModelSerializationTests/Animal.cs rename to sdk/core/Azure.Core/tests/ModelSerializationTests/Models/Animal.cs index a4612d4257ed..6234e1637cc0 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/Animal.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/Animal.cs @@ -9,12 +9,12 @@ namespace Azure.Core.Tests.ModelSerializationTests { - public class Animal : IJsonSerializable, IUtf8JsonSerializable + public class Animal : IJsonSerializable, IUtf8JsonSerializable, IModelInternalSerializable { private Dictionary RawData { get; set; } = new Dictionary(); public bool IsHungry { get; set; } = false; - public double Weight { get; set; } = 1.1; + public double Weight { get; set; } = 1.1d; public string LatinName { get; private set; } = "Animalia"; public string Name { get; set; } = "Animal"; @@ -152,6 +152,11 @@ public void Deserialize(Stream stream, SerializableOptions options = default) { JsonDocument jsonDocument = JsonDocument.Parse(stream); var model = DeserializeAnimal(jsonDocument.RootElement, options ?? new SerializableOptions()); + CopyModel(model); + } + + private void CopyModel(Animal model) + { this.LatinName = model.LatinName; this.Weight = model.Weight; this.IsHungry = model.IsHungry; @@ -178,6 +183,17 @@ public static Animal StaticDeserialize(Stream stream, SerializableOptions option JsonDocument jsonDocument = JsonDocument.Parse(stream); return DeserializeAnimal(jsonDocument.RootElement, options ?? new SerializableOptions()); } + + void IModelInternalSerializable.Serialize(Utf8JsonWriter writer, SerializableOptions options) + { + ((IUtf8JsonSerializable)this).Write(writer, options ?? new SerializableOptions()); + } + + void IModelInternalSerializable.Deserialize(ref Utf8JsonReader reader, SerializableOptions options) + { + var model = DeserializeAnimal(JsonDocument.ParseValue(ref reader).RootElement, options); + CopyModel(model); + } #endregion } } diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/CatReadOnlyProperty.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/CatReadOnlyProperty.cs similarity index 100% rename from sdk/core/Azure.Core/tests/ModelSerializationTests/CatReadOnlyProperty.cs rename to sdk/core/Azure.Core/tests/ModelSerializationTests/Models/CatReadOnlyProperty.cs diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/DogListProperty.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/DogListProperty.cs similarity index 92% rename from sdk/core/Azure.Core/tests/ModelSerializationTests/DogListProperty.cs rename to sdk/core/Azure.Core/tests/ModelSerializationTests/Models/DogListProperty.cs index 19c6943b6542..05e9d7a2f66d 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/DogListProperty.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/DogListProperty.cs @@ -203,15 +203,6 @@ internal static DogListProperty DeserializeDogListProperty(JsonElement element, internal class DogListPropertyConverter : JsonConverter { - public DogListPropertyConverter() - { - //I would like to have a custom property in the JsonSerializerOptions class but this doesn't seem to be there - //we also can't have a parameterless ctor without making this converter class public - //IgnoreAdditionalProperties = ignoreAdditionalProperties; - } - - //public bool IgnoreAdditionalProperties { get; } - public override DogListProperty Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return DeserializeDogListProperty(JsonDocument.ParseValue(ref reader).RootElement, ConvertOptions(options)); @@ -224,11 +215,13 @@ public override void Write(Utf8JsonWriter writer, DogListProperty value, JsonSer private SerializableOptions ConvertOptions(JsonSerializerOptions options) { - return new SerializableOptions() - { - //IgnoreAdditionalProperties = IgnoreAdditionalProperties, - IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties - }; + var serializableOptions = new SerializableOptions(); + //pulls the additional properties setting from the ModelJsonConverter if it exists + //if it does not exist it uses the default value of true for azure sdk use cases + var modelConverter = options.Converters.FirstOrDefault(c=>c.GetType() == typeof(ModelJsonConverter)) as ModelJsonConverter; + serializableOptions.IgnoreAdditionalProperties = modelConverter is not null ? modelConverter.IgnoreAdditionalProperties : true; + serializableOptions.IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties; + return serializableOptions; } } } diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/DogListPropertyBlankConverter.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/DogListPropertyBlankConverter.cs new file mode 100644 index 000000000000..b30675231e11 --- /dev/null +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/Models/DogListPropertyBlankConverter.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Azure.Core.Tests.ModelSerializationTests +{ + public class DogListPropertyBlankConverter : JsonConverter + { + public override DogListProperty Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var model = JsonDocument.ParseValue(ref reader); + return new DogListProperty(); + } + + public override void Write(Utf8JsonWriter writer, DogListProperty value, JsonSerializerOptions options) + { + return; + } + } +} diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/IUtf8JsonSerializable.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/TestClassCopies/IUtf8JsonSerializable.cs similarity index 100% rename from sdk/core/Azure.Core/tests/ModelSerializationTests/IUtf8JsonSerializable.cs rename to sdk/core/Azure.Core/tests/ModelSerializationTests/TestClassCopies/IUtf8JsonSerializable.cs diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/UsingJsonConverterTests.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/UsingJsonSerializerTests.cs similarity index 92% rename from sdk/core/Azure.Core/tests/ModelSerializationTests/UsingJsonConverterTests.cs rename to sdk/core/Azure.Core/tests/ModelSerializationTests/UsingJsonSerializerTests.cs index e23f5b9104fc..8da64ccf2e18 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/UsingJsonConverterTests.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/UsingJsonSerializerTests.cs @@ -8,7 +8,7 @@ namespace Azure.Core.Tests.ModelSerializationTests { - public class UsingJsonConverterTests + public class UsingJsonSerializerTests { [TestCase(true)] [TestCase(false)] @@ -59,8 +59,7 @@ public void DeserializeTest(bool ignoreReadonlyProperties) var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; Assert.IsNotNull(additionalProperties); - Assert.IsTrue(additionalProperties.ContainsKey("numberOfLegs")); - Assert.AreEqual("4", additionalProperties["numberOfLegs"].ToString()); + Assert.IsFalse(additionalProperties.ContainsKey("numberOfLegs")); string expected = "{"; if (!ignoreReadonlyProperties) @@ -71,7 +70,7 @@ public void DeserializeTest(bool ignoreReadonlyProperties) #else expected += "\"weight\":1.1,"; #endif - expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; var actual = JsonSerializer.Serialize(dog, options); Assert.AreEqual(expected, actual); diff --git a/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs b/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs index 583f898c3191..4ff0bf95e14e 100644 --- a/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs +++ b/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs @@ -200,5 +200,39 @@ public void ModelSerializerDeserialize() DogListProperty dog = ModelSerializer.Deserialize(json); #endregion } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void ModelConverterSerialize() + { + #region Snippet:ModelConverter_Serialize + DogListProperty dog = new DogListProperty + { + Name = "Doggo", + IsHungry = true, + Weight = 1.1, + FoodConsumed = { "kibble", "egg", "peanut butter" }, + }; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(false)); + + string json = JsonSerializer.Serialize(dog, options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void ModelConverterDeserialize() + { + #region Snippet:ModelConverter_Deserialize + string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(false)); + + DogListProperty dog = JsonSerializer.Deserialize(json, options); + #endregion + } } }