From a3278ccd6a7ac88c3c5ae85d46a3ec46a6f438ec Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 21 Mar 2015 20:35:57 +1300 Subject: [PATCH] -Fixed serializing discriminated unions with type name or reference tracking enabled -Fixed build --- .../Properties/AssemblyInfo.cs | 2 +- .../DiscriminatedUnionConverterTests.cs | 24 ++++++++++++- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/ContractResolverTests.cs | 4 +-- .../Serialization/JsonSerializerTest.cs | 4 +++ .../Converters/DiscriminatedUnionConverter.cs | 27 +++++++++------ .../Converters/IsoDateTimeConverter.cs | 6 +++- .../Converters/XmlNodeConverter.cs | 4 ++- .../Properties/AssemblyInfo.cs | 2 +- .../CamelCasePropertyNamesContractResolver.cs | 4 +-- .../Serialization/DefaultContractResolver.cs | 4 +-- Src/Newtonsoft.Json/Utilities/FSharpUtils.cs | 34 +++++++++++++++---- 12 files changed, 88 insertions(+), 29 deletions(-) diff --git a/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs index 0c50d011..aeb54af0 100644 --- a/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.TestConsole/Properties/AssemblyInfo.cs @@ -57,4 +57,4 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("7.0.0.0")] -[assembly: AssemblyFileVersion("7.0.1.18304")] +[assembly: AssemblyFileVersion("7.0.1.18321")] diff --git a/Src/Newtonsoft.Json.Tests/Converters/DiscriminatedUnionConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DiscriminatedUnionConverterTests.cs index 05b1e081..c08adfd3 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/DiscriminatedUnionConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/DiscriminatedUnionConverterTests.cs @@ -23,11 +23,11 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion +#if !(NET35 || NET20 || NETFX_CORE || ASPNETCORE50) using System.Diagnostics; using System.Reflection; using Microsoft.FSharp.Core; using Microsoft.FSharp.Reflection; -#if !(NET35 || NET20 || NETFX_CORE || ASPNETCORE50) using System; using System.Collections.Generic; using System.Linq; @@ -283,6 +283,28 @@ namespace Newtonsoft.Json.Tests.Converters { ExceptionAssert.Throws(() => JsonConvert.DeserializeObject(@"{""Case123"":""AUD""}"), "Unexpected property 'Case123' found when reading union. Path 'Case123', line 1, position 11."); } + + [Test] + public void SerializeUnionWithTypeNameHandlingAndReferenceTracking() + { + string json = JsonConvert.SerializeObject(Shape.NewRectangle(10.0, 5.0), new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + TypeNameHandling = TypeNameHandling.All + }); + + Console.WriteLine(json); + + Assert.AreEqual(@"{""Case"":""Rectangle"",""Fields"":[10.0,5.0]}", json); + + Shape c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(true, c.IsRectangle); + + Shape.Rectangle r = (Shape.Rectangle)c; + + Assert.AreEqual(5.0, r.length); + Assert.AreEqual(10.0, r.width); + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 4cdfcbb0..8b3cb370 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -76,4 +76,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("7.0.0.0")] -[assembly: AssemblyFileVersion("7.0.1.18304")] +[assembly: AssemblyFileVersion("7.0.1.18321")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs index feb0550d..b835e8d8 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -57,9 +57,9 @@ namespace Newtonsoft.Json.Tests.Serialization private readonly char _startingWithChar; public DynamicContractResolver(char startingWithChar) -#pragma warning disable 612 +#pragma warning disable 612,618 : base(false) -#pragma warning restore 612 +#pragma warning restore 612,618 { _startingWithChar = startingWithChar; } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 8b39c0ac..cb4f9715 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -3879,6 +3879,7 @@ Path '', line 1, position 1."); }", json); } +#if !NET20 public class DateTimeOffsetWrapper { public DateTimeOffset DateTimeOffsetValue { get; set; } @@ -3907,6 +3908,7 @@ Path '', line 1, position 1."); Assert.AreEqual(c.DateTimeOffsetValue, cISO.DateTimeOffsetValue); } +#endif [Test] public void CircularConstructorDeserialize() @@ -8684,6 +8686,7 @@ Path '', line 1, position 1."); Assert.AreEqual("derived", d.DerivedProperty); } +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) [Test] public void DeserializeNullableUnsignedLong() { @@ -8705,6 +8708,7 @@ Path '', line 1, position 1."); set; } } +#endif #if !NETFX_CORE [Test] diff --git a/Src/Newtonsoft.Json/Converters/DiscriminatedUnionConverter.cs b/Src/Newtonsoft.Json/Converters/DiscriminatedUnionConverter.cs index 01639085..134ecec4 100644 --- a/Src/Newtonsoft.Json/Converters/DiscriminatedUnionConverter.cs +++ b/Src/Newtonsoft.Json/Converters/DiscriminatedUnionConverter.cs @@ -49,7 +49,7 @@ namespace Newtonsoft.Json.Converters internal class Union { public List Cases; - public Converter TagReader { get; set; } + public FSharpFunction TagReader { get; set; } } internal class UnionCase @@ -57,8 +57,8 @@ namespace Newtonsoft.Json.Converters public int Tag; public string Name; public PropertyInfo[] Fields; - public Converter FieldReader; - public Converter Constructor; + public FSharpFunction FieldReader; + public FSharpFunction Constructor; } #endregion @@ -86,7 +86,7 @@ namespace Newtonsoft.Json.Converters { Union u = new Union(); - u.TagReader = (Converter)FSharpUtils.PreComputeUnionTagReader(null, t, null); + u.TagReader = (FSharpFunction)FSharpUtils.PreComputeUnionTagReader(null, t, null); u.Cases = new List(); object[] cases = (object[])FSharpUtils.GetUnionCases(null, t, null); @@ -97,8 +97,8 @@ namespace Newtonsoft.Json.Converters unionCase.Tag = (int)FSharpUtils.GetUnionCaseInfoTag(unionCaseInfo); unionCase.Name = (string)FSharpUtils.GetUnionCaseInfoName(unionCaseInfo); unionCase.Fields = (PropertyInfo[])FSharpUtils.GetUnionCaseInfoFields(unionCaseInfo); - unionCase.FieldReader = (Converter)FSharpUtils.PreComputeUnionReader(null, unionCaseInfo, null); - unionCase.Constructor = (Converter)FSharpUtils.PreComputeUnionConstructor(null, unionCaseInfo, null); + unionCase.FieldReader = (FSharpFunction)FSharpUtils.PreComputeUnionReader(null, unionCaseInfo, null); + unionCase.Constructor = (FSharpFunction)FSharpUtils.PreComputeUnionConstructor(null, unionCaseInfo, null); u.Cases.Add(unionCase); } @@ -119,7 +119,7 @@ namespace Newtonsoft.Json.Converters Type unionType = UnionTypeLookupCache.Get(value.GetType()); Union union = UnionCache.Get(unionType); - int tag = union.TagReader(value); + int tag = (int)union.TagReader.Invoke(value); UnionCase caseInfo = union.Cases.Single(c => c.Tag == tag); writer.WriteStartObject(); @@ -127,10 +127,15 @@ namespace Newtonsoft.Json.Converters writer.WriteValue(caseInfo.Name); if (caseInfo.Fields != null && caseInfo.Fields.Length > 0) { - object[] fields = caseInfo.FieldReader(value); + object[] fields = (object[])caseInfo.FieldReader.Invoke(value); writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(FieldsPropertyName) : FieldsPropertyName); - serializer.Serialize(writer, fields); + writer.WriteStartArray(); + foreach (object field in fields) + { + serializer.Serialize(writer, field); + } + writer.WriteEndArray(); } writer.WriteEndObject(); } @@ -210,7 +215,9 @@ namespace Newtonsoft.Json.Converters } } - return caseInfo.Constructor(typedFieldValues); + object[] args = { typedFieldValues }; + + return caseInfo.Constructor.Invoke(args); } /// diff --git a/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs b/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs index 85f6df97..79303149 100644 --- a/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs @@ -139,9 +139,13 @@ namespace Newtonsoft.Json.Converters #if !NET20 if (t == typeof(DateTimeOffset)) return (reader.Value is DateTimeOffset) ? reader.Value : new DateTimeOffset((DateTime)reader.Value); + + // converter is expected to return a DateTime + if (reader.Value is DateTimeOffset) + return ((DateTimeOffset)reader.Value).DateTime; #endif - return (reader.Value is DateTime) ? reader.Value : ((DateTimeOffset)reader.Value).DateTime; + return reader.Value; } if (reader.TokenType != JsonToken.String) diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs index aa47cffa..cfe5d0eb 100644 --- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -23,8 +23,10 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion -using System.Numerics; #if !PORTABLE40 +#if !(PORTABLE || NET20 || NET35) +using System.Numerics; +#endif using System; using System.Collections.Generic; using System.Globalization; diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 3626d992..2a740b80 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -92,5 +92,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("7.0.0.0")] -[assembly: AssemblyFileVersion("7.0.1.18304")] +[assembly: AssemblyFileVersion("7.0.1.18321")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs index 2658c0aa..8ffa3982 100644 --- a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs @@ -37,9 +37,9 @@ namespace Newtonsoft.Json.Serialization /// Initializes a new instance of the class. /// public CamelCasePropertyNamesContractResolver() -#pragma warning disable 612 +#pragma warning disable 612,618 : base(true) -#pragma warning restore 612 +#pragma warning restore 612,618 { } diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 7a67ecc6..5ca78eb1 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -95,9 +95,9 @@ namespace Newtonsoft.Json.Serialization /// public class DefaultContractResolver : IContractResolver { -#pragma warning disable 612 +#pragma warning disable 612,618 private static readonly IContractResolver _instance = new DefaultContractResolver(true); -#pragma warning restore 612 +#pragma warning restore 612,618 internal static IContractResolver Instance { diff --git a/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs b/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs index fdcb7dc8..13b0ee54 100644 --- a/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/FSharpUtils.cs @@ -34,6 +34,25 @@ using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Utilities { + internal class FSharpFunction + { + private readonly object _instance; + private readonly MethodCall _invoker; + + public FSharpFunction(object instance, MethodCall invoker) + { + _instance = instance; + _invoker = invoker; + } + + public object Invoke(params object[] args) + { + object o = _invoker(_instance, args); + + return o; + } + } + internal static class FSharpUtils { private static readonly object Lock = new object(); @@ -105,19 +124,20 @@ namespace Newtonsoft.Json.Utilities private static MethodCall CreateFSharpFuncCall(Type type, string methodName) { MethodInfo innerMethodInfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static); - MethodInfo toConvertMethodInfo = innerMethodInfo.ReturnType.GetMethod("ToConverter", BindingFlags.Public | BindingFlags.Static); + MethodInfo invokeFunc = innerMethodInfo.ReturnType.GetMethod("Invoke", BindingFlags.Public | BindingFlags.Instance); - MethodCall inner = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(innerMethodInfo); - MethodCall toConvert = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(toConvertMethodInfo); + MethodCall call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(innerMethodInfo); + MethodCall invoke = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(invokeFunc); - MethodCall methodCall = (target, args) => + MethodCall createFunction = (target, args) => { - object result = inner(target, args); + object result = call(target, args); - return toConvert(null, result); + FSharpFunction f = new FSharpFunction(result, invoke); + return f; }; - return methodCall; + return createFunction; } public static ObjectConstructor CreateSeq(Type t)