From c4d375dd6938b3e8b6efd9417d59d7c240e725f0 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 23 Mar 2013 12:48:41 +1300 Subject: [PATCH] -Fixed StringEnumConverter to throw a JsonSerializerException like the serializer throws for invalid enums -Changed Windows Phone and Portable builds to use Microsoft.CSharp.dll directly --- .../Converters/StringEnumConverterTests.cs | 26 +++++++++ .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 55 +++++++++++++++++++ .../Converters/StringEnumConverter.cs | 30 ++++++---- Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 2 - .../Properties/AssemblyInfo.cs | 2 +- .../JsonSerializerInternalReader.cs | 13 +---- Src/Newtonsoft.Json/Utilities/DynamicUtils.cs | 26 ++++++++- .../Utilities/MiscellaneousUtils.cs | 11 ++++ 9 files changed, 137 insertions(+), 30 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs index 71dcf77e..5caa46cc 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs @@ -24,6 +24,7 @@ #endregion using System; +using System.IO; using System.Runtime.Serialization; using Newtonsoft.Json.Converters; #if !NETFX_CORE @@ -334,5 +335,30 @@ namespace Newtonsoft.Json.Tests.Converters EnumClass c = JsonConvert.DeserializeObject(json, new StringEnumConverter()); Assert.IsNull(c.NullableStoreColor2); } + + public void DeserializeInvalidString() + { + string json = "{ \"Value\" : \"Three\" }"; + + ExceptionAssert.Throws( + @"Error converting value ""Three"" to type 'Newtonsoft.Json.Tests.Converters.StringEnumConverterTests+MyEnum'. Path 'Value', line 1, position 19.", + () => + { + var serializer = new JsonSerializer(); + serializer.Converters.Add(new StringEnumConverter()); + serializer.Deserialize(new JsonTextReader(new StringReader(json))); + }); + } + + public class Bucket + { + public MyEnum Value; + } + + public enum MyEnum + { + Alpha, + Beta, + } } } \ 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 be053098..fc0fb36a 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.1.15917")] +[assembly: AssemblyFileVersion("5.0.1.15923")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index d356d739..784c0257 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -90,6 +90,40 @@ namespace Newtonsoft.Json.Tests.Serialization [TestFixture] public class JsonSerializerTest : TestFixtureBase { + //[DataContract] + //public class BaseDataContract1 + //{ + // [DataMember(Name = "virtualMember")] + // public virtual string VirtualMember { get; set; } + + // [DataMember(Name = "nonVirtualMember")] + // public string NonVirtualMember { get; set; } + + // public virtual object NewMember { get; set; } + //} + + //public class ChildDataContract1 : BaseDataContract1 + //{ + // [DataMember(Name = "NewMember")] + // public virtual new string NewMember { get; set; } + // public override string VirtualMember { get; set; } + // public string AddedMember { get; set; } + //} + + //[Test] + //public void ChildDataContractTest1() + //{ + // var cc = new ChildDataContract1 + // { + // VirtualMember = "VirtualMember!", + // NonVirtualMember = "NonVirtualMember!", + // NewMember = "NewMember!" + // }; + + // string result = JsonConvert.SerializeObject(cc); + // Assert.AreEqual(@"{""NewMember"":""NewMember!"",""virtualMember"":""VirtualMember!"",""nonVirtualMember"":""NonVirtualMember!""}", result); + //} + [Test] public void PersonTypedObjectDeserialization() { @@ -6450,6 +6484,21 @@ Parameter name: value", }", json); } +#if !(NET20 || NET35) + public class IgnoreDataMemberTestClass + { + [IgnoreDataMember] + public int Ignored { get; set; } + } + + [Test] + public void IgnoreDataMemberTest() + { + string json = JsonConvert.SerializeObject(new IgnoreDataMemberTestClass() { Ignored = int.MaxValue }, Formatting.Indented); + Assert.AreEqual(@"{}", json); + } +#endif + #if !(NET20 || NET35) [Test] public void SerializeDataContractSerializationAttributes() @@ -6462,6 +6511,12 @@ Parameter name: value", IgnoreDataMemberAndDataMemberAttribute = "Value!" }; + //MemoryStream ms = new MemoryStream(); + //DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DataContractSerializationAttributesClass)); + //serializer.WriteObject(ms, dataContract); + + //Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); + string json = JsonConvert.SerializeObject(dataContract, Formatting.Indented); Assert.AreEqual(@"{ ""DataMemberAttribute"": ""Value!"", diff --git a/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs b/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs index cdf5a350..39001f8d 100644 --- a/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs +++ b/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs @@ -111,22 +111,30 @@ namespace Newtonsoft.Json.Converters return null; } - if (reader.TokenType == JsonToken.String) + try { - string enumText = reader.Value.ToString(); - if (enumText == string.Empty && isNullable) - return null; + if (reader.TokenType == JsonToken.String) + { + string enumText = reader.Value.ToString(); + if (enumText == string.Empty && isNullable) + return null; - var map = GetEnumNameMap(t); - string resolvedEnumName; - map.TryGetBySecond(enumText, out resolvedEnumName); - resolvedEnumName = resolvedEnumName ?? enumText; + var map = GetEnumNameMap(t); + string resolvedEnumName; + map.TryGetBySecond(enumText, out resolvedEnumName); + resolvedEnumName = resolvedEnumName ?? enumText; - return Enum.Parse(t, resolvedEnumName, true); + return Enum.Parse(t, resolvedEnumName, true); + } + + if (reader.TokenType == JsonToken.Integer) + return ConvertUtils.ConvertOrCast(reader.Value, CultureInfo.InvariantCulture, t); + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error converting value {0} to type '{1}'.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.FormatValueForPrint(reader.Value), objectType), ex); } - if (reader.TokenType == JsonToken.Integer) - return ConvertUtils.ConvertOrCast(reader.Value, CultureInfo.InvariantCulture, t); throw JsonSerializationException.Create(reader, "Unexpected token when parsing enum. Expected String or Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); } diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index c72eabe3..efdcc44f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -37,8 +37,6 @@ - - diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 2ce4f0e4..95f67f41 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -90,6 +90,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.1.15917")] +[assembly: AssemblyFileVersion("5.0.1.15923")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 6a14fde7..687f3418 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -671,24 +671,13 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } catch (Exception ex) { - throw JsonSerializationException.Create(reader, "Error converting value {0} to type '{1}'.".FormatWith(CultureInfo.InvariantCulture, FormatValueForPrint(value), targetType), ex); + throw JsonSerializationException.Create(reader, "Error converting value {0} to type '{1}'.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.FormatValueForPrint(value), targetType), ex); } } return value; } - private string FormatValueForPrint(object value) - { - if (value == null) - return "{null}"; - - if (value is string) - return @"""" + value + @""""; - - return value.ToString(); - } - private void SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, object target) { object currentValue; diff --git a/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs index a896d72f..c76a739a 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs @@ -29,7 +29,11 @@ using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Linq.Expressions; +#if !(PORTABLE || WINDOWS_PHONE) using System.Reflection; +#else +using Microsoft.CSharp.RuntimeBinder; +#endif using System.Runtime.CompilerServices; using System.Text; using System.Globalization; @@ -41,6 +45,7 @@ namespace Newtonsoft.Json.Utilities { internal static class BinderWrapper { +#if !(PORTABLE || WINDOWS_PHONE) #if !SILVERLIGHT public const string CSharpAssemblyName = "Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; #else @@ -95,9 +100,9 @@ namespace Newtonsoft.Json.Utilities private static void CreateMemberCalls() { - Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName); - Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName); - Type binderType = Type.GetType(BinderTypeName); + Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName, true); + Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName, true); + Type binderType = Type.GetType(BinderTypeName, true); Type csharpArgumentInfoTypeEnumerableType = typeof (IEnumerable<>).MakeGenericType(csharpArgumentInfoType); @@ -107,17 +112,32 @@ namespace Newtonsoft.Json.Utilities MethodInfo setMemberMethod = binderType.GetMethod("SetMember", new[] {csharpBinderFlagsType, typeof (string), typeof (Type), csharpArgumentInfoTypeEnumerableType}); _setMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(setMemberMethod); } +#endif public static CallSiteBinder GetMember(string name, Type context) { +#if !(PORTABLE || WINDOWS_PHONE) Init(); return (CallSiteBinder) _getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray); +#else + return Binder.GetMember( + CSharpBinderFlags.None, name, context, new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)}); +#endif } public static CallSiteBinder SetMember(string name, Type context) { +#if !(PORTABLE || WINDOWS_PHONE) Init(); return (CallSiteBinder) _setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray); +#else + return Binder.SetMember( + CSharpBinderFlags.None, name, context, new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) + }); +#endif } } diff --git a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs index 5b84bec4..dd55f35b 100644 --- a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs @@ -183,5 +183,16 @@ namespace Newtonsoft.Json.Utilities localName = qualifiedName.Substring(colonPosition + 1); } } + + internal static string FormatValueForPrint(object value) + { + if (value == null) + return "{null}"; + + if (value is string) + return @"""" + value + @""""; + + return value.ToString(); + } } } \ No newline at end of file