-Fixed serializing discriminated unions with type name or reference tracking enabled

-Fixed build
This commit is contained in:
James Newton-King
2015-03-21 20:35:57 +13:00
parent cdac4d8d38
commit a3278ccd6a
12 changed files with 88 additions and 29 deletions
@@ -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")]
@@ -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<JsonSerializationException>(() => JsonConvert.DeserializeObject<Currency>(@"{""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<Shape>(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
@@ -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")]
@@ -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;
}
@@ -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]
@@ -49,7 +49,7 @@ namespace Newtonsoft.Json.Converters
internal class Union
{
public List<UnionCase> Cases;
public Converter<object, int> 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<object, object[]> FieldReader;
public Converter<object[], object> Constructor;
public FSharpFunction FieldReader;
public FSharpFunction Constructor;
}
#endregion
@@ -86,7 +86,7 @@ namespace Newtonsoft.Json.Converters
{
Union u = new Union();
u.TagReader = (Converter<object, int>)FSharpUtils.PreComputeUnionTagReader(null, t, null);
u.TagReader = (FSharpFunction)FSharpUtils.PreComputeUnionTagReader(null, t, null);
u.Cases = new List<UnionCase>();
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<object, object[]>)FSharpUtils.PreComputeUnionReader(null, unionCaseInfo, null);
unionCase.Constructor = (Converter<object[], object>)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);
}
/// <summary>
@@ -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)
@@ -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;
@@ -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)]
@@ -37,9 +37,9 @@ namespace Newtonsoft.Json.Serialization
/// Initializes a new instance of the <see cref="CamelCasePropertyNamesContractResolver"/> class.
/// </summary>
public CamelCasePropertyNamesContractResolver()
#pragma warning disable 612
#pragma warning disable 612,618
: base(true)
#pragma warning restore 612
#pragma warning restore 612,618
{
}
@@ -95,9 +95,9 @@ namespace Newtonsoft.Json.Serialization
/// </summary>
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
{
+27 -7
View File
@@ -34,6 +34,25 @@ using Newtonsoft.Json.Serialization;
namespace Newtonsoft.Json.Utilities
{
internal class FSharpFunction
{
private readonly object _instance;
private readonly MethodCall<object, object> _invoker;
public FSharpFunction(object instance, MethodCall<object, object> 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<object, object> 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<object, object> inner = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(innerMethodInfo);
MethodCall<object, object> toConvert = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(toConvertMethodInfo);
MethodCall<object, object> call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(innerMethodInfo);
MethodCall<object, object> invoke = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(invokeFunc);
MethodCall<object, object> methodCall = (target, args) =>
MethodCall<object, object> 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<object> CreateSeq(Type t)