From 1b63698c0274f0a05327f33c24e46805c0383c6b Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 24 May 2013 19:16:02 +1200 Subject: [PATCH] -Fixed serializing and deserializing flag enums with EnumMember attribute -Fixed using a type converter on interfaces -Fixed deserializing ISerializable objects that also implement IConvertible -Fixed build --- .../Converters/StringEnumConverterTests.cs | 136 ++++-- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 405 ++++++++++++++++++ .../Converters/StringEnumConverter.cs | 56 ++- Src/Newtonsoft.Json/JsonConvert.cs | 18 - .../Newtonsoft.Json.WinRT.csproj | 2 + .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 28 +- Src/Newtonsoft.Json/Utilities/ConvertUtils.cs | 6 +- 9 files changed, 592 insertions(+), 63 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs index 1e0e1d74..1ed30fbf 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs @@ -24,8 +24,11 @@ #endregion using System; +using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Runtime.Serialization; +using System.Text; using System.Text.RegularExpressions; using Newtonsoft.Json.Converters; #if !NETFX_CORE @@ -74,6 +77,7 @@ namespace Newtonsoft.Json.Tests.Converters { [EnumMember(Value = "@first")] First, + [EnumMember(Value = "@second")] Second, Third @@ -83,6 +87,7 @@ namespace Newtonsoft.Json.Tests.Converters { [EnumMember(Value = "Third")] First, + [EnumMember(Value = "@second")] Second, Third @@ -100,24 +105,24 @@ namespace Newtonsoft.Json.Tests.Converters public void NamedEnumDuplicateTest() { ExceptionAssert.Throws("Enum name 'Third' already exists on enum 'NamedEnumDuplicate'.", - () => - { - EnumContainer c = new EnumContainer - { - Enum = NamedEnumDuplicate.First - }; + () => + { + EnumContainer c = new EnumContainer + { + Enum = NamedEnumDuplicate.First + }; - JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter()); - }); + JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter()); + }); } [Test] public void SerializeNameEnumTest() { EnumContainer c = new EnumContainer - { - Enum = NamedEnum.First - }; + { + Enum = NamedEnum.First + }; string json = JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter()); Assert.AreEqual(@"{ @@ -125,9 +130,9 @@ namespace Newtonsoft.Json.Tests.Converters }", json); c = new EnumContainer - { - Enum = NamedEnum.Third - }; + { + Enum = NamedEnum.Third + }; json = JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter()); Assert.AreEqual(@"{ @@ -179,7 +184,7 @@ namespace Newtonsoft.Json.Tests.Converters enumClass.NullableStoreColor1 = StoreColor.DarkGoldenrod; enumClass.NullableStoreColor2 = null; - string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter { CamelCaseText = true }); + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter {CamelCaseText = true}); Assert.AreEqual(@"{ ""StoreColor"": ""red"", @@ -192,8 +197,8 @@ namespace Newtonsoft.Json.Tests.Converters public void SerializeEnumClassUndefined() { EnumClass enumClass = new EnumClass(); - enumClass.StoreColor = (StoreColor)1000; - enumClass.NullableStoreColor1 = (StoreColor)1000; + enumClass.StoreColor = (StoreColor) 1000; + enumClass.NullableStoreColor1 = (StoreColor) 1000; enumClass.NullableStoreColor2 = null; string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter()); @@ -248,7 +253,7 @@ namespace Newtonsoft.Json.Tests.Converters NegativeEnumClass negativeEnumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); Assert.AreEqual(NegativeEnum.Negative, negativeEnumClass.Value1); - Assert.AreEqual((NegativeEnum)int.MinValue, negativeEnumClass.Value2); + Assert.AreEqual((NegativeEnum) int.MinValue, negativeEnumClass.Value2); } [Test] @@ -263,7 +268,7 @@ namespace Newtonsoft.Json.Tests.Converters EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); Assert.AreEqual(StoreColor.Red | StoreColor.White, enumClass.StoreColor); - Assert.AreEqual((StoreColor)0, enumClass.NullableStoreColor1); + Assert.AreEqual((StoreColor) 0, enumClass.NullableStoreColor1); Assert.AreEqual(StoreColor.Red | StoreColor.White | StoreColor.Black, enumClass.NullableStoreColor2); } @@ -294,8 +299,8 @@ namespace Newtonsoft.Json.Tests.Converters EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); - Assert.AreEqual((StoreColor)1000, enumClass.StoreColor); - Assert.AreEqual((StoreColor)1000, enumClass.NullableStoreColor1); + Assert.AreEqual((StoreColor) 1000, enumClass.StoreColor); + Assert.AreEqual((StoreColor) 1000, enumClass.NullableStoreColor1); Assert.AreEqual(null, enumClass.NullableStoreColor2); } @@ -303,11 +308,11 @@ namespace Newtonsoft.Json.Tests.Converters public void CamelCaseTextFlagEnumSerialization() { EnumContainer c = new EnumContainer - { - Enum = FlagsTestEnum.First | FlagsTestEnum.Second - }; + { + Enum = FlagsTestEnum.First | FlagsTestEnum.Second + }; - string json = JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter { CamelCaseText = true }); + string json = JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter {CamelCaseText = true}); Assert.AreEqual(@"{ ""Enum"": ""first, second"" }", json); @@ -320,7 +325,7 @@ namespace Newtonsoft.Json.Tests.Converters ""Enum"": ""first, second"" }"; - EnumContainer c = JsonConvert.DeserializeObject>(json, new StringEnumConverter { CamelCaseText = true }); + EnumContainer c = JsonConvert.DeserializeObject>(json, new StringEnumConverter {CamelCaseText = true}); Assert.AreEqual(FlagsTestEnum.First | FlagsTestEnum.Second, c.Enum); } @@ -362,5 +367,84 @@ namespace Newtonsoft.Json.Tests.Converters Alpha, Beta, } + +#if !NET20 + [Test] + public void EnumMemberPlusFlags() + { + List lfoo = + new List + { + Foo.Bat | Foo.SerializeAsBaz, + Foo.FooBar, + Foo.Bat, + Foo.SerializeAsBaz, + Foo.FooBar | Foo.SerializeAsBaz, + (Foo) int.MaxValue + }; + + string json1 = JsonConvert.SerializeObject(lfoo, Formatting.Indented, new StringEnumConverter {CamelCaseText = true}); + + Assert.AreEqual(@"[ + ""Bat, baz"", + ""foo_bar"", + ""Bat"", + ""baz"", + ""foo_bar, baz"", + 2147483647 +]", json1); + + IList foos = JsonConvert.DeserializeObject>(json1); + + Assert.AreEqual(6, foos.Count); + Assert.AreEqual(Foo.Bat | Foo.SerializeAsBaz, foos[0]); + Assert.AreEqual(Foo.FooBar, foos[1]); + Assert.AreEqual(Foo.Bat, foos[2]); + Assert.AreEqual(Foo.SerializeAsBaz, foos[3]); + Assert.AreEqual(Foo.FooBar | Foo.SerializeAsBaz, foos[4]); + Assert.AreEqual((Foo) int.MaxValue, foos[5]); + + List lbar = new List() {Bar.FooBar, Bar.Bat, Bar.SerializeAsBaz}; + + string json2 = JsonConvert.SerializeObject(lbar, Formatting.Indented, new StringEnumConverter {CamelCaseText = true}); + + Assert.AreEqual(@"[ + ""foo_bar"", + ""Bat"", + ""baz"" +]", json2); + + IList bars = JsonConvert.DeserializeObject>(json2); + + Assert.AreEqual(3, bars.Count); + Assert.AreEqual(Bar.FooBar, bars[0]); + Assert.AreEqual(Bar.Bat, bars[1]); + Assert.AreEqual(Bar.SerializeAsBaz, bars[2]); + } + + // Define other methods and classes here + [Flags] + [JsonConverter(typeof (StringEnumConverter))] + private enum Foo + { + [EnumMember(Value = "foo_bar")] + FooBar = 0x01, + Bat = 0x02, + + [EnumMember(Value = "baz")] + SerializeAsBaz = 0x4, + }; + + [JsonConverter(typeof (StringEnumConverter))] + private enum Bar + { + [EnumMember(Value = "foo_bar")] + FooBar, + Bat, + + [EnumMember(Value = "baz")] + SerializeAsBaz + }; +#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 5f1f8699..e024fff4 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.5.16119")] +[assembly: AssemblyFileVersion("5.0.5.16124")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index e2cad03f..80bc8aa6 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -84,6 +84,7 @@ using System.Linq; #endif #if !(SILVERLIGHT || NETFX_CORE) using System.Drawing; +using System.Diagnostics; #endif namespace Newtonsoft.Json.Tests.Serialization @@ -159,6 +160,22 @@ namespace Newtonsoft.Json.Tests.Serialization } } +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + [Test] + public void DeserializeISerializableIConvertible() + { + Ratio ratio = new Ratio(2, 1); + string json = JsonConvert.SerializeObject(ratio); + + Assert.AreEqual(@"{""n"":2,""d"":1}", json); + + Ratio ratio2 = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(ratio.Denominator, ratio2.Denominator); + Assert.AreEqual(ratio.Numerator, ratio2.Numerator); + } +#endif + [Test] public void SerializeDeserializeRegex() { @@ -8846,6 +8863,181 @@ Parameter name: value", Assert.AreEqual(john, jane.Spouse); Assert.AreEqual(jane, john.Spouse); } + +#if !(NETFX_CORE || NET35 || NET20 || PORTABLE || PORTABLE40) + [TypeConverter(typeof(MyInterfaceConverter))] + internal interface IMyInterface + { + string Name { get; } + + string PrintTest(); + } + + internal class MyInterfaceConverter : TypeConverter + { + private readonly List _writers = new List + { + new ConsoleWriter(), + new TraceWriter() + }; + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(string); + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value == null) + return null; + + return (from w in _writers where w.Name == value.ToString() select w).FirstOrDefault(); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, + Type destinationType) + { + if (value == null) + return null; + return ((IMyInterface)value).Name; + } + } + + [TypeConverter(typeof(MyInterfaceConverter))] + internal class ConsoleWriter : IMyInterface + { + public string Name + { + get { return "Console Writer"; } + } + + public string PrintTest() + { + return "ConsoleWriter"; + } + + public override string ToString() + { + return Name; + } + } + + internal class TraceWriter : IMyInterface + { + public string Name + { + get { return "Trace Writer"; } + } + + public override string ToString() + { + return Name; + } + + public string PrintTest() + { + return "TraceWriter"; + } + } + + internal class TypeConverterJsonConverter : JsonConverter + { + private TypeConverter GetConverter(Type type) + { + var converters = type.GetCustomAttributes(typeof(TypeConverterAttribute), true).Union( + from t in type.GetInterfaces() + from c in t.GetCustomAttributes(typeof(TypeConverterAttribute), true) + select c).Distinct(); + + return + (from c in converters + let converter = + (TypeConverter)Activator.CreateInstance(Type.GetType(((TypeConverterAttribute)c).ConverterTypeName)) + where converter.CanConvertFrom(typeof(string)) + && converter.CanConvertTo(typeof(string)) + select converter) + .FirstOrDefault(); + } + + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var converter = GetConverter(value.GetType()); + var text = converter.ConvertToInvariantString(value); + + writer.WriteValue(text); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var converter = GetConverter(objectType); + return converter.ConvertFromInvariantString(reader.Value.ToString()); + } + + public override bool CanConvert(Type objectType) + { + return GetConverter(objectType) != null; + } + } + + [Test] + public void TypeConverterOnInterface() + { + var consoleWriter = new ConsoleWriter(); + + // If dynamic type handling is enabled, case 1 and 3 work fine + var options = new JsonSerializerSettings + { + Converters = new JsonConverterCollection {new TypeConverterJsonConverter()}, + //TypeNameHandling = TypeNameHandling.All + }; + + // + // Case 1: Serialize the concrete value and restore it from the interface + // Therefore we need dynamic handling of type information if the type is not serialized with the type converter directly + // + var text1 = JsonConvert.SerializeObject(consoleWriter, Formatting.Indented, options); + Assert.AreEqual(@"""Console Writer""", text1); + + var restoredWriter = JsonConvert.DeserializeObject(text1, options); + Assert.AreEqual("ConsoleWriter", restoredWriter.PrintTest()); + + // + // Case 2: Serialize a dictionary where the interface is the key + // The key is always serialized with its ToString() method and therefore needs a mechanism to be restored from that (using the type converter) + // + var dict2 = new Dictionary(); + dict2.Add(consoleWriter, "Console"); + + var text2 = JsonConvert.SerializeObject(dict2, Formatting.Indented, options); + Assert.AreEqual(@"{ + ""Console Writer"": ""Console"" +}", text2); + + var restoredObject = JsonConvert.DeserializeObject>(text2, options); + Assert.AreEqual("ConsoleWriter", restoredObject.First().Key.PrintTest()); + + // + // Case 3 Serialize a dictionary where the interface is the value + // The key is always serialized with its ToString() method and therefore needs a mechanism to be restored from that (using the type converter) + // + var dict3 = new Dictionary(); + dict3.Add("Console", consoleWriter); + + var text3 = JsonConvert.SerializeObject(dict3, Formatting.Indented, options); + Assert.AreEqual(@"{ + ""Console"": ""Console Writer"" +}", text3); + + var restoredDict2 = JsonConvert.DeserializeObject>(text3, options); + Assert.AreEqual("ConsoleWriter", restoredDict2.First().Value.PrintTest()); + } +#endif } public class PersonReference @@ -9350,4 +9542,217 @@ Parameter name: value", UserName = samAccountName.Split('\\')[1]; } } + +#if !NETFX_CORE + public struct Ratio : IConvertible, IFormattable, ISerializable + { + private readonly int _numerator; + private readonly int _denominator; + + public Ratio(int numerator, int denominator) + { + _numerator = numerator; + _denominator = denominator; + } + + #region Properties + + public int Numerator + { + get { return _numerator; } + } + + public int Denominator + { + get { return _denominator; } + } + + public bool IsNan + { + get { return _denominator == 0; } + } + + #endregion + + #region Serialization operations + + public Ratio(SerializationInfo info, StreamingContext context) + { + _numerator = info.GetInt32("n"); + _denominator = info.GetInt32("d"); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("n", _numerator); + info.AddValue("d", _denominator); + } + + #endregion + + #region IConvertible Members + + public TypeCode GetTypeCode() + { + return TypeCode.Object; + } + + public bool ToBoolean(IFormatProvider provider) + { + return this._numerator == 0; + } + + public byte ToByte(IFormatProvider provider) + { + return (byte)(this._numerator / this._denominator); + } + + public char ToChar(IFormatProvider provider) + { + return Convert.ToChar(this._numerator / this._denominator); + } + + public DateTime ToDateTime(IFormatProvider provider) + { + return Convert.ToDateTime(this._numerator / this._denominator); + } + + public decimal ToDecimal(IFormatProvider provider) + { + return (decimal)this._numerator / this._denominator; + } + + public double ToDouble(IFormatProvider provider) + { + return this._denominator == 0 + ? double.NaN + : (double)this._numerator / this._denominator; + } + + public short ToInt16(IFormatProvider provider) + { + return (short)(this._numerator / this._denominator); + } + + public int ToInt32(IFormatProvider provider) + { + return this._numerator / this._denominator; + } + + public long ToInt64(IFormatProvider provider) + { + return this._numerator / this._denominator; + } + + public sbyte ToSByte(IFormatProvider provider) + { + return (sbyte)(this._numerator / this._denominator); + } + + public float ToSingle(IFormatProvider provider) + { + return this._denominator == 0 + ? float.NaN + : (float)this._numerator / this._denominator; + } + + public string ToString(IFormatProvider provider) + { + return this._denominator == 1 + ? this._numerator.ToString(provider) + : this._numerator.ToString(provider) + "/" + this._denominator.ToString(provider); + } + + public object ToType(Type conversionType, IFormatProvider provider) + { + return Convert.ChangeType(ToDouble(provider), conversionType, provider); + } + + public ushort ToUInt16(IFormatProvider provider) + { + return (ushort)(this._numerator / this._denominator); + } + + public uint ToUInt32(IFormatProvider provider) + { + return (uint)(this._numerator / this._denominator); + } + + public ulong ToUInt64(IFormatProvider provider) + { + return (ulong)(this._numerator / this._denominator); + } + + #endregion + + #region String operations + + public override string ToString() + { + return ToString(CultureInfo.InvariantCulture); + } + + public string ToString(string format, IFormatProvider formatProvider) + { + return ToString(CultureInfo.InvariantCulture); + } + + public static Ratio Parse(string input) + { + return Parse(input, CultureInfo.InvariantCulture); + } + + public static Ratio Parse(string input, IFormatProvider formatProvider) + { + Ratio result; + if (!TryParse(input, formatProvider, out result)) + { + throw new FormatException( + string.Format( + CultureInfo.InvariantCulture, + "Text '{0}' is invalid text representation of ratio", + input)); + } + return result; + } + + public static bool TryParse(string input, out Ratio result) + { + return TryParse(input, CultureInfo.InvariantCulture, out result); + } + + public static bool TryParse(string input, IFormatProvider formatProvider, out Ratio result) + { + if (input != null) + { + var fractionIndex = input.IndexOf('/'); + + int numerator; + if (fractionIndex < 0) + { + if (int.TryParse(input, NumberStyles.Integer, formatProvider, out numerator)) + { + result = new Ratio(numerator, 1); + return true; + } + } + else + { + int denominator; + if (int.TryParse(input.Substring(0, fractionIndex), NumberStyles.Integer, formatProvider, out numerator) && + int.TryParse(input.Substring(fractionIndex + 1), NumberStyles.Integer, formatProvider, out denominator)) + { + result = new Ratio(numerator, denominator); + return true; + } + } + } + + result = default(Ratio); + return false; + } + + #endregion + } +#endif } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs b/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs index 1cb6cac4..cdbf21d3 100644 --- a/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs +++ b/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs @@ -77,17 +77,24 @@ namespace Newtonsoft.Json.Converters { BidirectionalDictionary map = GetEnumNameMap(e.GetType()); - string resolvedEnumName; - map.TryGetByFirst(enumName, out resolvedEnumName); - resolvedEnumName = resolvedEnumName ?? enumName; - - if (CamelCaseText) + string[] names = enumName.Split(','); + for (int i = 0; i < names.Length; i++) { - string[] names = resolvedEnumName.Split(',').Select(item => StringUtils.ToCamelCase(item.Trim())).ToArray(); - resolvedEnumName = string.Join(", ", names); + string name = names[i].Trim(); + + string resolvedEnumName; + map.TryGetByFirst(name, out resolvedEnumName); + resolvedEnumName = resolvedEnumName ?? name; + + if (CamelCaseText) + resolvedEnumName = StringUtils.ToCamelCase(resolvedEnumName); + + names[i] = resolvedEnumName; } - writer.WriteValue(resolvedEnumName); + string finalName = string.Join(", ", names); + + writer.WriteValue(finalName); } } @@ -120,12 +127,27 @@ namespace Newtonsoft.Json.Converters if (enumText == string.Empty && isNullable) return null; - var map = GetEnumNameMap(t); - string resolvedEnumName; - map.TryGetBySecond(enumText, out resolvedEnumName); - resolvedEnumName = resolvedEnumName ?? enumText; + string finalEnumText; - return Enum.Parse(t, resolvedEnumName, true); + BidirectionalDictionary map = GetEnumNameMap(t); + if (enumText.IndexOf(',') != -1) + { + string[] names = enumText.Split(','); + for (int i = 0; i < names.Length; i++) + { + string name = names[i].Trim(); + + names[i] = ResolvedEnumName(map, name); + } + + finalEnumText = string.Join(", ", names); + } + else + { + finalEnumText = ResolvedEnumName(map, enumText); + } + + return Enum.Parse(t, finalEnumText, true); } if (reader.TokenType == JsonToken.Integer) @@ -140,6 +162,14 @@ namespace Newtonsoft.Json.Converters throw JsonSerializationException.Create(reader, "Unexpected token when parsing enum. Expected String or Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); } + private static string ResolvedEnumName(BidirectionalDictionary map, string enumText) + { + string resolvedEnumName; + map.TryGetBySecond(enumText, out resolvedEnumName); + resolvedEnumName = resolvedEnumName ?? enumText; + return resolvedEnumName; + } + private BidirectionalDictionary GetEnumNameMap(Type t) { BidirectionalDictionary map; diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index b5bcee98..847d7dff 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -490,24 +490,6 @@ namespace Newtonsoft.Json throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); } - internal static bool IsJsonPrimitiveType(Type t) - { - PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(t); - - if (typeCode != PrimitiveTypeCode.Empty && typeCode != PrimitiveTypeCode.Object) - return true; - -#if !(PORTABLE || NETFX_CORE) - if (typeof(IConvertible).IsAssignableFrom(t) - || (ReflectionUtils.IsNullableType(t) && typeof(IConvertible).IsAssignableFrom(Nullable.GetUnderlyingType(t)))) - { - return !typeof(JToken).IsAssignableFrom(t); - } -#endif - - return false; - } - #region Serialize /// /// Serializes the specified object to a JSON string. diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj index 3f502a45..9f567606 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj @@ -251,6 +251,8 @@ + + diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index b5bf5d75..978cc099 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -92,6 +92,6 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.5.16119")] +[assembly: AssemblyFileVersion("5.0.5.16124")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 961a686c..89eabdd7 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -844,7 +844,7 @@ namespace Newtonsoft.Json.Serialization { Type t = ReflectionUtils.EnsureNotNullableType(objectType); - if (JsonConvert.IsJsonPrimitiveType(objectType)) + if (IsJsonPrimitiveType(objectType)) return CreatePrimitiveContract(objectType); if (JsonTypeReflector.GetJsonObjectAttribute(t) != null) @@ -878,9 +878,35 @@ namespace Newtonsoft.Json.Serialization return CreateDynamicContract(objectType); #endif +#if !(PORTABLE || NETFX_CORE) + // tested last because it is not possible to automatically deserialize custom IConvertible types + if (IsIConvertible(t)) + return CreatePrimitiveContract(t); +#endif + return CreateObjectContract(objectType); } + internal static bool IsJsonPrimitiveType(Type t) + { + PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(t); + + return (typeCode != PrimitiveTypeCode.Empty && typeCode != PrimitiveTypeCode.Object); + } + +#if !(PORTABLE || NETFX_CORE) + internal static bool IsIConvertible(Type t) + { + if (typeof(IConvertible).IsAssignableFrom(t) + || (ReflectionUtils.IsNullableType(t) && typeof(IConvertible).IsAssignableFrom(Nullable.GetUnderlyingType(t)))) + { + return !typeof(JToken).IsAssignableFrom(t); + } + + return false; + } +#endif + internal static bool CanConvertToString(Type type) { #if !(NETFX_CORE || PORTABLE40 || PORTABLE) diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 10aef2fa..95bd854b 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -407,9 +407,6 @@ namespace Newtonsoft.Json.Utilities return FromBigInteger((BigInteger)initialValue, targetType); #endif - if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract()) - throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType"); - #if !(NETFX_CORE || PORTABLE40 || PORTABLE) // see if source or target types have a TypeConverter that converts between the two TypeConverter toConverter = GetConverter(initialType); @@ -449,6 +446,9 @@ namespace Newtonsoft.Json.Utilities return EnsureTypeAssignable(ToValue((INullable)initialValue), initialType, targetType); #endif + if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract()) + throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType"); + throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialType, targetType)); }