From: Marco Rossignoli Date: Wed, 12 Jun 2019 15:08:09 +0000 (+0200) Subject: [3.0 System.Text.Json]JsonExtensionData should allow non-JsonElement types during... X-Git-Tag: submit/tizen/20210909.063632~11031^2~1320 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f61d6e51eaacf36a0f54b322e33764188a764162;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [3.0 System.Text.Json]JsonExtensionData should allow non-JsonElement types during serialization (dotnet/corefx#38306) * fix serialization * serialize all as dictionary * address PR feedback * address PR feedback * remove redundant test Commit migrated from https://github.com/dotnet/corefx/commit/4e16caa67a6b15ce270c231720177818f50f1f84 --- diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index ab7ec94..9e06826 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -356,9 +356,6 @@ The collection type '{0}' is not supported. - - The data extension property '{0}.{1}' cannot contain dictionary values of type '{2}'. Dictionary values must be of type JsonElement. - The collection type '{0}' on '{1}' is not supported. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs index 94cbae9..a7ee061 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.HandleDictionary.cs @@ -27,7 +27,7 @@ namespace System.Text.Json if (enumerable == null) { // Write a null object or enumerable. - state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, writeNull : true); + state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, writeNull: true); return true; } @@ -37,36 +37,28 @@ namespace System.Text.Json if (state.Current.Enumerator.MoveNext()) { - // Handle DataExtension. - if (ReferenceEquals(jsonPropertyInfo, state.Current.JsonClassInfo.DataExtensionProperty)) + // Check for polymorphism. + if (elementClassInfo.ClassType == ClassType.Unknown) { - WriteExtensionData(writer, ref state.Current); + object currentValue = ((IDictionaryEnumerator)state.Current.Enumerator).Entry.Value; + GetRuntimeClassInfo(currentValue, ref elementClassInfo, options); + } + + if (elementClassInfo.ClassType == ClassType.Value) + { + elementClassInfo.GetPolicyProperty().WriteDictionary(ref state.Current, writer); + } + else if (state.Current.Enumerator.Current == null) + { + writer.WriteNull(jsonPropertyInfo.Name); } else { - // Check for polymorphism. - if (elementClassInfo.ClassType == ClassType.Unknown) - { - object currentValue = ((IDictionaryEnumerator)state.Current.Enumerator).Entry.Value; - GetRuntimeClassInfo(currentValue, ref elementClassInfo, options); - } - - if (elementClassInfo.ClassType == ClassType.Value) - { - elementClassInfo.GetPolicyProperty().WriteDictionary(ref state.Current, writer); - } - else if (state.Current.Enumerator.Current == null) - { - writer.WriteNull(jsonPropertyInfo.Name); - } - else - { - // An object or another enumerator requires a new stack frame. - var enumerator = (IDictionaryEnumerator)state.Current.Enumerator; - object value = enumerator.Value; - state.Push(elementClassInfo, value); - state.Current.KeyName = (string)enumerator.Key; - } + // An object or another enumerator requires a new stack frame. + var enumerator = (IDictionaryEnumerator)state.Current.Enumerator; + object value = enumerator.Value; + state.Push(elementClassInfo, value); + state.Current.KeyName = (string)enumerator.Key; } return false; @@ -134,21 +126,5 @@ namespace System.Text.Json converter.Write(escapedKey, value, writer); } } - - private static void WriteExtensionData(Utf8JsonWriter writer, ref WriteStackFrame frame) - { - DictionaryEntry entry = ((IDictionaryEnumerator)frame.Enumerator).Entry; - if (entry.Value is JsonElement element) - { - Debug.Assert(entry.Key is string); - - string propertyName = (string)entry.Key; - element.WriteProperty(propertyName, writer); - } - else - { - ThrowHelper.ThrowInvalidOperationException_SerializationDataExtensionPropertyInvalid(frame.JsonClassInfo, entry.Value.GetType()); - } - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index c4f7f69..bd505b5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -118,12 +118,6 @@ namespace System.Text.Json } [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowInvalidOperationException_SerializationDataExtensionPropertyInvalid(JsonClassInfo jsonClassInfo, Type invalidType) - { - throw new InvalidOperationException(SR.Format(SR.SerializationDataExtensionPropertyInvalidElement, jsonClassInfo.Type, jsonClassInfo.DataExtensionProperty.PropertyInfo.Name, invalidType)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidOperationException_DeserializeMissingParameterlessConstructor(Type invalidType) { throw new NotSupportedException(SR.Format(SR.DeserializeMissingParameterlessConstructor, invalidType)); diff --git a/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs b/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs index c45ed4e..b28af0e 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/ExtensionDataTests.cs @@ -178,23 +178,63 @@ namespace System.Text.Json.Serialization.Tests } [Fact] - public static void InvalidExtensionValue() + public static void ExtensionPropertyObjectValue_Empty() + { + // Baseline + ClassWithExtensionPropertyAlreadyInstantiated obj = JsonSerializer.Parse(@"{}"); + Assert.Equal(@"{""MyOverflow"":{}}", JsonSerializer.ToString(obj)); + } + + [Fact] + public static void ExtensionPropertyObjectValue() { // Baseline ClassWithExtensionPropertyAlreadyInstantiated obj = JsonSerializer.Parse(@"{}"); obj.MyOverflow.Add("test", new object()); + obj.MyOverflow.Add("test1", 1); - try - { - JsonSerializer.ToString(obj); - Assert.True(false, "InvalidOperationException should have thrown."); - } - catch (InvalidOperationException e) - { - // Verify the exception contains the property name and invalid type. - Assert.Contains("ClassWithExtensionPropertyAlreadyInstantiated.MyOverflow", e.Message); - Assert.Contains("System.Object", e.Message); - } + Assert.Equal(@"{""MyOverflow"":{""test"":{},""test1"":1}}", JsonSerializer.ToString(obj)); + } + + [Fact] + public static void ExtensionPropertyObjectValue_RoundTrip() + { + // Baseline + ClassWithExtensionPropertyAlreadyInstantiated obj = JsonSerializer.Parse(@"{}"); + obj.MyOverflow.Add("test", new object()); + obj.MyOverflow.Add("test1", 1); + obj.MyOverflow.Add("test2", "text"); + obj.MyOverflow.Add("test3", new DummyObj() { Prop = "ObjectProp" }); + obj.MyOverflow.Add("test4", new DummyStruct() { Prop = "StructProp" }); + obj.MyOverflow.Add("test5", new Dictionary() { { "Key", "Value" }, { "Key1", "Value1" }, }); + + ClassWithExtensionPropertyAlreadyInstantiated roundTripObj = JsonSerializer.Parse(JsonSerializer.ToString(obj)); + + Assert.Equal(6, roundTripObj.MyOverflow.Count); + + Assert.IsType(roundTripObj.MyOverflow["test"]); + Assert.IsType(roundTripObj.MyOverflow["test1"]); + Assert.IsType(roundTripObj.MyOverflow["test2"]); + Assert.IsType(roundTripObj.MyOverflow["test3"]); + + Assert.Equal(JsonValueType.Object, ((JsonElement)roundTripObj.MyOverflow["test"]).Type); + + Assert.Equal(JsonValueType.Number, ((JsonElement)roundTripObj.MyOverflow["test1"]).Type); + Assert.Equal(1, ((JsonElement)roundTripObj.MyOverflow["test1"]).GetInt32()); + Assert.Equal(1, ((JsonElement)roundTripObj.MyOverflow["test1"]).GetInt64()); + + Assert.Equal(JsonValueType.String, ((JsonElement)roundTripObj.MyOverflow["test2"]).Type); + Assert.Equal("text", ((JsonElement)roundTripObj.MyOverflow["test2"]).GetString()); + + Assert.Equal(JsonValueType.Object, ((JsonElement)roundTripObj.MyOverflow["test3"]).Type); + Assert.Equal("ObjectProp", ((JsonElement)roundTripObj.MyOverflow["test3"]).GetProperty("Prop").GetString()); + + Assert.Equal(JsonValueType.Object, ((JsonElement)roundTripObj.MyOverflow["test4"]).Type); + Assert.Equal("StructProp", ((JsonElement)roundTripObj.MyOverflow["test4"]).GetProperty("Prop").GetString()); + + Assert.Equal(JsonValueType.Object, ((JsonElement)roundTripObj.MyOverflow["test5"]).Type); + Assert.Equal("Value", ((JsonElement)roundTripObj.MyOverflow["test5"]).GetProperty("Key").GetString()); + Assert.Equal("Value1", ((JsonElement)roundTripObj.MyOverflow["test5"]).GetProperty("Key1").GetString()); } [Fact] @@ -214,6 +254,26 @@ namespace System.Text.Json.Serialization.Tests Assert.Equal(3, child.MyOverflow["MyIntMissingChild"].GetInt32()); } + [Fact] + public static void ExtensionProperty_InvalidDictionary() + { + ClassWithInvalidExtensionPropertyStringString obj1 = new ClassWithInvalidExtensionPropertyStringString(); + Assert.Throws(() => JsonSerializer.ToString(obj1)); + + ClassWithInvalidExtensionPropertyObjectString obj2 = new ClassWithInvalidExtensionPropertyObjectString(); + Assert.Throws(() => JsonSerializer.ToString(obj2)); + } + + public class DummyObj + { + public string Prop { get; set; } + } + + public struct DummyStruct + { + public string Prop { get; set; } + } + public class ClassWithExtensionPropertyAlreadyInstantiated { public ClassWithExtensionPropertyAlreadyInstantiated() @@ -246,6 +306,18 @@ namespace System.Text.Json.Serialization.Tests public Dictionary MyOverflow { get; set; } } + public class ClassWithInvalidExtensionPropertyStringString + { + [JsonExtensionData] + public Dictionary MyOverflow { get; set; } + } + + public class ClassWithInvalidExtensionPropertyObjectString + { + [JsonExtensionData] + public Dictionary MyOverflow { get; set; } + } + public class ClassWithTwoExtensionPropertys { [JsonExtensionData]