From 358070ce45d26776a2925594c47d9073f777c525 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 26 Mar 2020 18:53:20 -0400 Subject: [PATCH] Close issues fixed by serializer refactoring (#33819) * Close issues fixed by serializer refactoring * Validate ext test results * Fix CI issue * Address review feedback --- .../tests/Serialization/ExtensionDataTests.cs | 14 +++-- .../tests/Serialization/NullableTests.cs | 42 +++++++++++++ .../TestClasses.GenericCollections.cs | 56 ++++++++++++++--- .../Value.ReadTests.GenericCollections.cs | 61 ++++++++++++++++++- 4 files changed, 158 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs b/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs index f9a3ce5cc9aee0..a43c7069933df0 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs @@ -770,12 +770,16 @@ private class ClassWithExtensionPropertyCustomIImmutableJsonElement public GenericIImmutableDictionaryWrapper MyOverflow { get; set; } } - [Fact] - public static void DeserializeIntoGenericDictionaryParameterCount() + [Theory] + [InlineData(typeof(ClassWithExtensionPropertyNoGenericParameters))] + [InlineData(typeof(ClassWithExtensionPropertyOneGenericParameter))] + [InlineData(typeof(ClassWithExtensionPropertyThreeGenericParameters))] + public static void DeserializeIntoGenericDictionaryParameterCount(Type type) { - JsonSerializer.Deserialize("{\"hello\":\"world\"}"); - JsonSerializer.Deserialize("{\"hello\":\"world\"}"); - JsonSerializer.Deserialize("{\"hello\":\"world\"}"); + object obj = JsonSerializer.Deserialize("{\"hello\":\"world\"}", type); + + IDictionary extData = (IDictionary)type.GetProperty("MyOverflow").GetValue(obj)!; + Assert.Equal("world", ((JsonElement)extData["hello"]).GetString()); } private class ClassWithExtensionPropertyNoGenericParameters diff --git a/src/libraries/System.Text.Json/tests/Serialization/NullableTests.cs b/src/libraries/System.Text.Json/tests/Serialization/NullableTests.cs index cfc994293edb37..be88618f5b1fb4 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/NullableTests.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/NullableTests.cs @@ -394,5 +394,47 @@ IEnumerator IEnumerable.GetEnumerator() return ((IDictionary)dict).GetEnumerator(); } } + + [Fact] + public static void NullableCustomStructRoundtrip() + { + string serialized = JsonSerializer.Serialize(new ClassWithNullablePerson + { + Person = new Person + { + FirstName = "John", + Age = 24 + } + }); + Assert.Contains(@"{""Person"":{", serialized); + Assert.Contains(@"""FirstName"":""John""", serialized); + Assert.Contains(@"""Age"":24", serialized); + Assert.Contains(@"""Birthday"":null", serialized); + Assert.DoesNotContain(@"""Value"":{""", serialized); + Assert.DoesNotContain(@"""HasValue"":""", serialized); + + var obj = JsonSerializer.Deserialize(serialized); + Assert.Equal("John", obj.Person?.FirstName); + Assert.Equal(24, obj.Person?.Age); + Assert.Null(obj.Person?.Birthday); + + serialized = JsonSerializer.Serialize(new ClassWithNullablePerson()); + Assert.Equal(@"{""Person"":null}", serialized); + + obj = JsonSerializer.Deserialize(serialized); + Assert.Null(obj.Person); + } + + public class ClassWithNullablePerson + { + public Person? Person { get; set; } + } + + public struct Person + { + public string FirstName { get; set; } + public int? Age { get; set; } + public DateTime? Birthday { get; set; } + } } } diff --git a/src/libraries/System.Text.Json/tests/Serialization/TestClasses.GenericCollections.cs b/src/libraries/System.Text.Json/tests/Serialization/TestClasses.GenericCollections.cs index e30595f574776c..f428afbb797156 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/TestClasses.GenericCollections.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/TestClasses.GenericCollections.cs @@ -1349,17 +1349,57 @@ public StringToStringSortedDictionaryWrapper(IList> } } - public class StringToGenericSortedDictionary : SortedDictionary + public class HashSetWithBackingCollection : ICollection { - public StringToGenericSortedDictionary() { } + private readonly ICollection _inner; - // For populating test data only. We cannot assume actual input will have this method. - public StringToGenericSortedDictionary(IList> items) + public HashSetWithBackingCollection() { - foreach (KeyValuePair item in items) - { - Add(item.Key, item.Value); - } + _inner = new HashSet(); + } + + public HashSetWithBackingCollection(IEnumerable values) + { + _inner = new HashSet(values); + } + + public int Count => _inner.Count; + + public bool IsReadOnly => _inner.IsReadOnly; + + public void Add(string item) + { + _inner.Add(item); + } + + public void Clear() + { + _inner.Clear(); + } + + public bool Contains(string item) + { + return _inner.Contains(item); + } + + public void CopyTo(string[] array, int arrayIndex) + { + _inner.CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return _inner.GetEnumerator(); + } + + public bool Remove(string item) + { + return _inner.Remove(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _inner.GetEnumerator(); } } } diff --git a/src/libraries/System.Text.Json/tests/Serialization/Value.ReadTests.GenericCollections.cs b/src/libraries/System.Text.Json/tests/Serialization/Value.ReadTests.GenericCollections.cs index 42ac095a387ecd..3071a9b79c36c9 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/Value.ReadTests.GenericCollections.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/Value.ReadTests.GenericCollections.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Specialized; using System.Linq; -using System; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -1220,5 +1218,64 @@ public static void Read_Generic_NoPublicConstructor_Throws(Type type, string jso NotSupportedException ex = Assert.Throws(() => JsonSerializer.Deserialize(json, type)); Assert.Contains(type.ToString(), ex.Message); } + + [Fact] + public static void DoesNotCall_CollectionPropertyGetter_EveryTimeElementIsAdded() + { + var networkList = new List { "Network1", "Network2" }; + + string serialized = JsonSerializer.Serialize(new NetworkWrapper { NetworkList = networkList }); + Assert.Equal(@"{""NetworkList"":[""Network1"",""Network2""]}", serialized); + + NetworkWrapper obj = JsonSerializer.Deserialize(serialized); + + int i = 0; + foreach (string network in obj.NetworkList) + { + Assert.Equal(networkList[i], network); + i++; + } + } + + public class NetworkWrapper + { + private string _Networks = string.Empty; + + [JsonIgnore] + public string Networks + { + get => _Networks; + set => _Networks = value ?? string.Empty; + } + + public IEnumerable NetworkList + { + get => Networks.Split(','); + set => Networks = value != null ? string.Join(",", value) : ""; + } + } + + [Fact] + public static void CollectionWith_BackingField_CanRoundtrip() + { + string json = "{\"AllowedGrantTypes\":[\"client_credentials\"]}"; + + Client obj = JsonSerializer.Deserialize(json); + Assert.Equal("client_credentials", obj.AllowedGrantTypes.First()); + + string serialized = JsonSerializer.Serialize(obj); + Assert.Equal(json, serialized); + } + + private class Client + { + private ICollection _allowedGrantTypes = new HashSetWithBackingCollection(); + + public ICollection AllowedGrantTypes + { + get { return _allowedGrantTypes; } + set { _allowedGrantTypes = new HashSetWithBackingCollection(value); } + } + } } }