-Fixed deserializing F# unions with out of order properties

-Tests
This commit is contained in:
James Newton-King
2014-09-05 20:36:45 +12:00
parent 6cf71b805e
commit ffd9cf9209
9 changed files with 629 additions and 44 deletions
@@ -0,0 +1,131 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
#if !(NET35 || NET20 || NETFX_CORE)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Tests.TestObjects;
using NUnit.Framework;
namespace Newtonsoft.Json.Tests.Converters
{
[TestFixture]
public class DiscriminatedUnionConverterTests : TestFixtureBase
{
[Test]
public void SerializeBasicUnion()
{
string json = JsonConvert.SerializeObject(Currency.AUD);
Assert.AreEqual(@"{""Case"":""AUD"",""Fields"":[]}", json);
}
[Test]
public void SerializeUnionWithFields()
{
string json = JsonConvert.SerializeObject(Shape.NewRectangle(10.0, 5.0));
Assert.AreEqual(@"{""Case"":""Rectangle"",""Fields"":[10.0,5.0]}", json);
}
[Test]
public void DeserializeBasicUnion()
{
Currency c = JsonConvert.DeserializeObject<Currency>(@"{""Case"":""AUD"",""Fields"":[]}");
Assert.AreEqual(Currency.AUD, c);
c = JsonConvert.DeserializeObject<Currency>(@"{""Fields"":[],""Case"":""EUR""}");
Assert.AreEqual(Currency.EUR, c);
c = JsonConvert.DeserializeObject<Currency>(@"null");
Assert.AreEqual(null, c);
}
[Test]
public void DeserializeUnionWithFields()
{
Shape c = JsonConvert.DeserializeObject<Shape>(@"{""Case"":""Rectangle"",""Fields"":[10.0,5.0]}");
Assert.AreEqual(true, c.IsRectangle);
Shape.Rectangle r = (Shape.Rectangle)c;
Assert.AreEqual(5.0, r.length);
Assert.AreEqual(10.0, r.width);
}
[Test]
public void DeserializeBasicUnion_NoMatch()
{
ExceptionAssert.Throws<JsonSerializationException>("No union type found with the name 'abcdefg'. Path 'Case', line 1, position 17.",
() => JsonConvert.DeserializeObject<Currency>(@"{""Case"":""abcdefg"",""Fields"":[]}"));
}
[Test]
public void DeserializeBasicUnion_MismatchedFieldCount()
{
ExceptionAssert.Throws<JsonSerializationException>("The number of field values does not match the number of properties definied by union 'AUD'. Path '', line 1, position 27.",
() => JsonConvert.DeserializeObject<Currency>(@"{""Case"":""AUD"",""Fields"":[1]}"));
}
[Test]
public void DeserializeBasicUnion_NoCaseName()
{
ExceptionAssert.Throws<JsonSerializationException>("No 'Case' property with union name found. Path '', line 1, position 14.",
() => JsonConvert.DeserializeObject<Currency>(@"{""Fields"":[1]}"));
}
[Test]
public void DeserializeBasicUnion_NoFields()
{
ExceptionAssert.Throws<JsonSerializationException>("No 'Fields' property with union fields found. Path '', line 1, position 14.",
() => JsonConvert.DeserializeObject<Currency>(@"{""Case"":""AUD""}"));
}
[Test]
public void DeserializeBasicUnion_UnexpectedEnd()
{
ExceptionAssert.Throws<JsonSerializationException>("Unexpected end when reading union. Path 'Case', line 1, position 8.",
() => JsonConvert.DeserializeObject<Currency>(@"{""Case"":"));
}
[Test]
public void DeserializeBasicUnion_FieldsObject()
{
ExceptionAssert.Throws<JsonSerializationException>("Union fields must been an array. Path 'Fields', line 1, position 24.",
() => JsonConvert.DeserializeObject<Currency>(@"{""Case"":""AUD"",""Fields"":{}}"));
}
[Test]
public void DeserializeBasicUnion_UnexpectedProperty()
{
ExceptionAssert.Throws<JsonSerializationException>("Unexpected property 'Case123' found when reading union. Path 'Case123', line 1, position 11.",
() => JsonConvert.DeserializeObject<Currency>(@"{""Case123"":""AUD""}"));
}
}
}
#endif
@@ -34,7 +34,31 @@ namespace Newtonsoft.Json.Tests.Converters
string json = JsonConvert.SerializeObject(values, Formatting.Indented);
Console.WriteLine(json);
Assert.AreEqual(@"[
{
""Key"": ""123"",
""Value"": 123
},
{
""Key"": ""456"",
""Value"": 456
}
]", json);
IList<KeyValuePair<string, int>> v2 = JsonConvert.DeserializeObject<IList<KeyValuePair<string, int>>>(json);
Assert.AreEqual(2, v2.Count);
Assert.AreEqual("123", v2[0].Key);
Assert.AreEqual(123, v2[0].Value);
Assert.AreEqual("456", v2[1].Key);
Assert.AreEqual(456, v2[1].Value);
}
[Test]
public void DeserializeUnexpectedEnd()
{
ExceptionAssert.Throws<JsonSerializationException>("Unexpected end when reading KeyValuePair. Path 'Key', line 1, position 14.",
() => JsonConvert.DeserializeObject<KeyValuePair<string, int>>(@"{""Key"": ""123"","));
}
}
}
@@ -68,6 +68,7 @@
<Compile Include="Converters\CustomCreationConverterTests.cs" />
<Compile Include="Converters\DataSetConverterTests.cs" />
<Compile Include="Converters\DataTableConverterTests.cs" />
<Compile Include="Converters\DiscriminatedUnionConverterTests.cs" />
<Compile Include="Converters\ExpandoObjectConverterTests.cs" />
<Compile Include="Converters\IsoDateTimeConverterTests.cs" />
<Compile Include="Converters\JavaScriptDateTimeConverterTests.cs" />
@@ -292,6 +293,7 @@
<Compile Include="TestObjects\ConverableMembers.cs" />
<Compile Include="TestObjects\ConverterPrecedenceClass.cs" />
<Compile Include="TestObjects\ConverterPrecedenceClassConverter.cs" />
<Compile Include="TestObjects\Currency.cs" />
<Compile Include="TestObjects\CustomerDataSet.cs">
<SubType>Component</SubType>
</Compile>
@@ -383,6 +385,7 @@
<Compile Include="TestObjects\SerializationEventTestObjectWithConstructor.cs" />
<Compile Include="TestObjects\SetOnlyPropertyClass.cs" />
<Compile Include="TestObjects\SetOnlyPropertyClass2.cs" />
<Compile Include="TestObjects\Shape.cs" />
<Compile Include="TestObjects\Shortie.cs" />
<Compile Include="TestObjects\Store.cs" />
<Compile Include="TestObjects\StoreColor.cs" />
@@ -75,4 +75,4 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.4.17703")]
[assembly: AssemblyFileVersion("6.0.4.17705")]
@@ -0,0 +1,173 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
#if !(NET35 || NET20 || NETFX_CORE)
using Microsoft.FSharp.Core;
using System;
using System.Collections;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Newtonsoft.Json.Tests.TestObjects
{
[Serializable, DebuggerDisplay("{__DebugDisplay(),nq}"), CompilationMapping(SourceConstructFlags.SumType)]
public class Currency
{
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly int _tag;
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal static readonly Currency _unique_AUD = new Currency(1);
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal static readonly Currency _unique_EUR = new Currency(4);
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal static readonly Currency _unique_JPY = new Currency(5);
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal static readonly Currency _unique_LocalCurrency = new Currency(0);
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal static readonly Currency _unique_NZD = new Currency(2);
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal static readonly Currency _unique_USD = new Currency(3);
[CompilerGenerated, DebuggerNonUserCode]
internal Currency(int _tag)
{
this._tag = _tag;
}
[CompilerGenerated, DebuggerNonUserCode]
internal object __DebugDisplay()
{
return ExtraTopLevelOperators.PrintFormatToString<FSharpFunc<Currency, string>>(new PrintfFormat<FSharpFunc<Currency, string>, Unit, string, string, string>("%+0.8A")).Invoke(this);
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static Currency AUD
{
[CompilationMapping(SourceConstructFlags.UnionCase, 1)]
get { return _unique_AUD; }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static Currency EUR
{
[CompilationMapping(SourceConstructFlags.UnionCase, 4)]
get { return _unique_EUR; }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsAUD
{
[CompilerGenerated, DebuggerNonUserCode]
get { return (this.Tag == 1); }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsEUR
{
[CompilerGenerated, DebuggerNonUserCode]
get { return (this.Tag == 4); }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsJPY
{
[CompilerGenerated, DebuggerNonUserCode]
get { return (this.Tag == 5); }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsLocalCurrency
{
[CompilerGenerated, DebuggerNonUserCode]
get { return (this.Tag == 0); }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsNZD
{
[CompilerGenerated, DebuggerNonUserCode]
get { return (this.Tag == 2); }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsUSD
{
[CompilerGenerated, DebuggerNonUserCode]
get { return (this.Tag == 3); }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static Currency JPY
{
[CompilationMapping(SourceConstructFlags.UnionCase, 5)]
get { return _unique_JPY; }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static Currency LocalCurrency
{
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
get { return _unique_LocalCurrency; }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static Currency NZD
{
[CompilationMapping(SourceConstructFlags.UnionCase, 2)]
get { return _unique_NZD; }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public int Tag
{
[CompilerGenerated, DebuggerNonUserCode]
get { return this._tag; }
}
[CompilerGenerated, DebuggerNonUserCode, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static Currency USD
{
[CompilationMapping(SourceConstructFlags.UnionCase, 3)]
get { return _unique_USD; }
}
public static class Tags
{
public const int AUD = 1;
public const int EUR = 4;
public const int JPY = 5;
public const int LocalCurrency = 0;
public const int NZD = 2;
public const int USD = 3;
}
}
}
#endif
@@ -0,0 +1,227 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
#if !(NET35 || NET20 || NETFX_CORE)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.FSharp.Core;
namespace Newtonsoft.Json.Tests.TestObjects
{
[Serializable, DebuggerDisplay("{__DebugDisplay(),nq}"), CompilationMapping(SourceConstructFlags.SumType)]
public class Shape
{
// Fields
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly int _tag;
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal static readonly Shape _unique_Empty;
static Shape()
{
_unique_Empty = new Shape(3);
}
[CompilerGenerated]
internal Shape(int _tag)
{
this._tag = _tag;
}
[CompilationMapping(SourceConstructFlags.UnionCase, 1)]
public static Shape NewCircle(double _radius)
{
return new Circle(_radius);
}
[CompilationMapping(SourceConstructFlags.UnionCase, 2)]
public static Shape NewPrism(double _width, double item2, double _height)
{
return new Prism(_width, item2, _height);
}
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
public static Shape NewRectangle(double _width, double _length)
{
return new Rectangle(_width, _length);
}
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static Shape Empty
{
[CompilationMapping(SourceConstructFlags.UnionCase, 3)]
get { return _unique_Empty; }
}
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsCircle
{
[CompilerGenerated]
get { return (this.Tag == 1); }
}
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsEmpty
{
[CompilerGenerated]
get { return (this.Tag == 3); }
}
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsPrism
{
[CompilerGenerated]
get { return (this.Tag == 2); }
}
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsRectangle
{
[CompilerGenerated]
get { return (this.Tag == 0); }
}
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)]
public int Tag
{
[CompilerGenerated]
get { return this._tag; }
}
[Serializable, DebuggerDisplay("{__DebugDisplay(),nq}")]
public class Circle : Shape
{
// Fields
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly double _radius;
// Methods
[CompilerGenerated, DebuggerNonUserCode]
internal Circle(double _radius) : base(1)
{
this._radius = _radius;
}
// Properties
[CompilationMapping(SourceConstructFlags.Field, 1, 0), CompilerGenerated, DebuggerNonUserCode]
public double radius
{
[CompilerGenerated, DebuggerNonUserCode]
get { return this._radius; }
}
}
[Serializable, DebuggerDisplay("{__DebugDisplay(),nq}")]
public class Prism : Shape
{
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly double _height;
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly double _width;
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly double item2;
[CompilerGenerated, DebuggerNonUserCode]
internal Prism(double _width, double item2, double _height) : base(2)
{
this._width = _width;
this.item2 = item2;
this._height = _height;
}
[CompilationMapping(SourceConstructFlags.Field, 2, 2), CompilerGenerated, DebuggerNonUserCode]
public double height
{
[CompilerGenerated, DebuggerNonUserCode]
get { return this._height; }
}
[CompilationMapping(SourceConstructFlags.Field, 2, 1), CompilerGenerated, DebuggerNonUserCode]
public double Item2
{
[CompilerGenerated, DebuggerNonUserCode]
get { return this.item2; }
}
[CompilationMapping(SourceConstructFlags.Field, 2, 0), CompilerGenerated, DebuggerNonUserCode]
public double width
{
[CompilerGenerated, DebuggerNonUserCode]
get { return this._width; }
}
}
[Serializable, DebuggerDisplay("{__DebugDisplay(),nq}")]
public class Rectangle : Shape
{
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly double _length;
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated]
internal readonly double _width;
[CompilerGenerated, DebuggerNonUserCode]
internal Rectangle(double _width, double _length) : base(0)
{
this._width = _width;
this._length = _length;
}
[CompilationMapping(SourceConstructFlags.Field, 0, 1), CompilerGenerated, DebuggerNonUserCode]
public double length
{
[CompilerGenerated, DebuggerNonUserCode]
get { return this._length; }
}
[CompilationMapping(SourceConstructFlags.Field, 0, 0), CompilerGenerated, DebuggerNonUserCode]
public double width
{
[CompilerGenerated, DebuggerNonUserCode]
get { return this._width; }
}
}
public static class Tags
{
// Fields
public const int Circle = 1;
public const int Empty = 3;
public const int Prism = 2;
public const int Rectangle = 0;
}
}
}
#endif
@@ -24,6 +24,7 @@
#endregion
#if !(NET35 || NET20 || NETFX_CORE)
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -86,44 +87,72 @@ namespace Newtonsoft.Json.Converters
if (reader.TokenType == JsonToken.Null)
return null;
IEnumerable cases = (IEnumerable)FSharpUtils.GetUnionCases(null, objectType, null);
ReadAndAssertProperty(reader, CasePropertyName);
ReadAndAssert(reader);
string caseName = reader.Value.ToString();
object matchingCaseInfo = null;
foreach (object c in cases)
string caseName = null;
JArray fields = null;
// start object
ReadAndAssert(reader);
while (reader.TokenType == JsonToken.PropertyName)
{
if ((string)FSharpUtils.GetUnionCaseInfoName(c) == caseName)
string propertyName = reader.Value.ToString();
if (string.Equals(propertyName, CasePropertyName, StringComparison.OrdinalIgnoreCase))
{
matchingCaseInfo = c;
break;
ReadAndAssert(reader);
IEnumerable cases = (IEnumerable)FSharpUtils.GetUnionCases(null, objectType, null);
caseName = reader.Value.ToString();
foreach (object c in cases)
{
if ((string)FSharpUtils.GetUnionCaseInfoName(c) == caseName)
{
matchingCaseInfo = c;
break;
}
}
if (matchingCaseInfo == null)
throw JsonSerializationException.Create(reader, "No union type found with the name '{0}'.".FormatWith(CultureInfo.InvariantCulture, caseName));
}
}
else if (string.Equals(propertyName, FieldsPropertyName, StringComparison.OrdinalIgnoreCase))
{
ReadAndAssert(reader);
if (reader.TokenType != JsonToken.StartArray)
throw JsonSerializationException.Create(reader, "Union fields must been an array.");
if (matchingCaseInfo == null)
throw new JsonSerializationException("No union type found with the name '{0}'.".FormatWith(CultureInfo.InvariantCulture, caseName));
fields = (JArray)JToken.ReadFrom(reader);
}
else
{
throw JsonSerializationException.Create(reader, "Unexpected property '{0}' found when reading union.".FormatWith(CultureInfo.InvariantCulture, propertyName));
}
ReadAndAssertProperty(reader, FieldsPropertyName);
// start array
ReadAndAssert(reader);
// first value
ReadAndAssert(reader);
PropertyInfo[] fieldProperties = (PropertyInfo[])FSharpUtils.GetUnionCaseInfoFields(matchingCaseInfo);
List<object> fieldValues = new List<object>();
foreach (PropertyInfo field in fieldProperties)
{
fieldValues.Add(serializer.Deserialize(reader, field.PropertyType));
ReadAndAssert(reader);
}
// end object
ReadAndAssert(reader);
if (matchingCaseInfo == null)
throw JsonSerializationException.Create(reader, "No '{0}' property with union name found.".FormatWith(CultureInfo.InvariantCulture, CasePropertyName));
if (fields == null)
throw JsonSerializationException.Create(reader, "No '{0}' property with union fields found.".FormatWith(CultureInfo.InvariantCulture, FieldsPropertyName));
return FSharpUtils.MakeUnion(null, matchingCaseInfo, fieldValues.ToArray(), null);
PropertyInfo[] fieldProperties = (PropertyInfo[])FSharpUtils.GetUnionCaseInfoFields(matchingCaseInfo);
if (fieldProperties.Length != fields.Count)
throw JsonSerializationException.Create(reader, "The number of field values does not match the number of properties definied by union '{0}'.".FormatWith(CultureInfo.InvariantCulture, caseName));
object[] typedFieldValues = new object[fieldProperties.Length];
for (int i = 0; i < fields.Count; i++)
{
JToken t = fields[i];
PropertyInfo fieldProperty = fieldProperties[i];
typedFieldValues[i] = t.ToObject(fieldProperty.PropertyType);
}
return FSharpUtils.MakeUnion(null, matchingCaseInfo, typedFieldValues, null);
}
/// <summary>
@@ -166,18 +195,10 @@ namespace Newtonsoft.Json.Converters
return (bool)FSharpUtils.IsUnion(null, objectType, null);
}
private static void ReadAndAssertProperty(JsonReader reader, string propertyName)
{
ReadAndAssert(reader);
if (reader.TokenType != JsonToken.PropertyName || !string.Equals(reader.Value.ToString(), propertyName, StringComparison.OrdinalIgnoreCase))
throw new JsonSerializationException("Expected JSON property '{0}'.".FormatWith(CultureInfo.InvariantCulture, propertyName));
}
private static void ReadAndAssert(JsonReader reader)
{
if (!reader.Read())
throw new JsonSerializationException("Unexpected end.");
throw JsonSerializationException.Create(reader, "Unexpected end when reading union.");
}
}
}
@@ -99,19 +99,19 @@ namespace Newtonsoft.Json.Converters
object key = null;
object value = null;
reader.Read();
ReadAndAssert(reader);
while (reader.TokenType == JsonToken.PropertyName)
{
string propertyName = reader.Value.ToString();
if (string.Equals(propertyName, KeyName, StringComparison.OrdinalIgnoreCase))
{
reader.Read();
ReadAndAssert(reader);
key = serializer.Deserialize(reader, reflectionObject.GetType(KeyName));
}
else if (string.Equals(propertyName, ValueName, StringComparison.OrdinalIgnoreCase))
{
reader.Read();
ReadAndAssert(reader);
value = serializer.Deserialize(reader, reflectionObject.GetType(ValueName));
}
else
@@ -119,7 +119,7 @@ namespace Newtonsoft.Json.Converters
reader.Skip();
}
reader.Read();
ReadAndAssert(reader);
}
return reflectionObject.Creator(key, value);
@@ -143,5 +143,11 @@ namespace Newtonsoft.Json.Converters
return false;
}
private static void ReadAndAssert(JsonReader reader)
{
if (!reader.Read())
throw JsonSerializationException.Create(reader, "Unexpected end when reading KeyValuePair.");
}
}
}
@@ -91,5 +91,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.4.17703")]
[assembly: AssemblyFileVersion("6.0.4.17705")]
[assembly: CLSCompliant(true)]