diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs index 3ff59abf..e592243b 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -119,6 +119,18 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(2, memberNames.Count); Assert.AreEqual("ChildValue", memberNames[0]); Assert.AreEqual("Hello Joe", memberNames[1]); + + o = new JObject( + new JProperty("ChildValue1", "blah blah"), + new JProperty("Hello Joe1", null)); + + d = o; + + memberNames = o.GetDynamicMemberNames().ToList(); + + Assert.AreEqual(2, memberNames.Count); + Assert.AreEqual("ChildValue1", memberNames[0]); + Assert.AreEqual("Hello Joe1", memberNames[1]); } [Test] @@ -126,11 +138,15 @@ namespace Newtonsoft.Json.Tests.Linq { AssertValueConverted(true); AssertValueConverted(true); + AssertValueConverted(false); + AssertValueConverted(null); AssertValueConverted(Encoding.UTF8.GetBytes("blah")); AssertValueConverted(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc)); AssertValueConverted(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc)); + AssertValueConverted(null); AssertValueConverted(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1))); AssertValueConverted(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1))); + AssertValueConverted(null); AssertValueConverted(99.9m); AssertValueConverted(99.9m); AssertValueConverted(99.9); @@ -150,6 +166,7 @@ namespace Newtonsoft.Json.Tests.Linq AssertValueConverted(ulong.MaxValue); AssertValueConverted(ushort.MinValue); AssertValueConverted(ushort.MinValue); + AssertValueConverted(null); } private static void AssertValueConverted(T value) @@ -157,7 +174,8 @@ namespace Newtonsoft.Json.Tests.Linq JValue v = new JValue(value); dynamic d = v; - Assert.AreEqual(value, (T)d); + T t = d; + Assert.AreEqual(value, t); } } } diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index 0ec68d93..fd3f41fb 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -267,7 +267,6 @@ - MSLinqToSQLGenerator LinqToSqlClasses.designer.cs diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index cc804918..82b5f58e 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -3827,63 +3827,5 @@ keyword such as type of business."" }); } #endif - - [Test] - public void SerializingIEnumerableOfTShouldRetainGenericTypeInfo() - { - CustomEnumerable products = new CustomEnumerable(); - - string json = JsonConvert.SerializeObject(products, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - - Assert.AreEqual(@"{ - ""$type"": ""Newtonsoft.Json.Tests.TestObjects.Product[], Newtonsoft.Json.Tests"", - ""$values"": [] -}", json); - } - - public class CustomEnumerable : IEnumerable - { - //NOTE: a simple linked list - private readonly T value; - private readonly CustomEnumerable next; - private readonly int count; - - private CustomEnumerable(T value, CustomEnumerable next) - { - this.value = value; - this.next = next; - count = this.next.count + 1; - } - - public CustomEnumerable() - { - count = 0; - } - - public CustomEnumerable AddFirst(T newVal) - { - return new CustomEnumerable(newVal, this); - } - - public IEnumerator GetEnumerator() - { - if (count == 0) // last node - yield break; - yield return value; - - var nextInLine = next; - while (nextInLine != null) - { - if (nextInLine.count != 0) - yield return nextInLine.value; - nextInLine = nextInLine.next; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index 9344d767..715b939b 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -24,6 +24,7 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization.Formatters; @@ -640,6 +641,68 @@ namespace Newtonsoft.Json.Tests.Serialization List statues = (List) newCollection["List"]; Assert.AreEqual(2, statues.Count); } + + + [Test] + public void SerializingIEnumerableOfTShouldRetainGenericTypeInfo() + { + string productClassRef = ReflectionUtils.GetTypeName(typeof(Product[]), FormatterAssemblyStyle.Simple); + + CustomEnumerable products = new CustomEnumerable(); + + string json = JsonConvert.SerializeObject(products, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); + + Assert.AreEqual(@"{ + ""$type"": """ + productClassRef + @""", + ""$values"": [] +}", json); + } + + public class CustomEnumerable : IEnumerable + { + //NOTE: a simple linked list + private readonly T value; + private readonly CustomEnumerable next; + private readonly int count; + + private CustomEnumerable(T value, CustomEnumerable next) + { + this.value = value; + this.next = next; + count = this.next.count + 1; + } + + public CustomEnumerable() + { + count = 0; + } + + public CustomEnumerable AddFirst(T newVal) + { + return new CustomEnumerable(newVal, this); + } + + public IEnumerator GetEnumerator() + { + if (count == 0) // last node + yield break; + yield return value; + + var nextInLine = next; + while (nextInLine != null) + { + if (nextInLine.count != 0) + yield return nextInLine.value; + nextInLine = nextInLine.next; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + } public class Message diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 4f375624..cf77c5d5 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -346,7 +346,7 @@ namespace Newtonsoft.Json _currentState = State.Finished; break; default: - throw new JsonReaderException("While setting the reader state back to current object an unexpected JsonType was encountered: " + currentObject); + throw new JsonReaderException("While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, currentObject)); } } @@ -408,7 +408,7 @@ namespace Newtonsoft.Json case JsonToken.EndConstructor: return JTokenType.Constructor; default: - throw new JsonReaderException("Not a valid close JsonToken: " + token); + throw new JsonReaderException("Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token)); } } diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 308f2db0..8299dfa7 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -666,23 +666,19 @@ namespace Newtonsoft.Json.Linq /// protected override DynamicMetaObject GetMetaObject(Expression parameter) { - return new DynamicProxyMetaObject(parameter, new JObjectDynamicProxy(this), true); + return new DynamicProxyMetaObject(parameter, this, new JObjectDynamicProxy(), true); } private class JObjectDynamicProxy : DynamicProxy { - public JObjectDynamicProxy(JObject value) : base(value) - { - } - - public override bool TryGetMember(GetMemberBinder binder, out object result) + public override bool TryGetMember(JObject instance, GetMemberBinder binder, out object result) { // result can be null - result = Value[binder.Name]; + result = instance[binder.Name]; return true; } - public override bool TrySetMember(SetMemberBinder binder, object value) + public override bool TrySetMember(JObject instance, SetMemberBinder binder, object value) { JToken v = value as JToken; @@ -690,13 +686,13 @@ namespace Newtonsoft.Json.Linq if (v == null) v = new JValue(value); - Value[binder.Name] = v; + instance[binder.Name] = v; return true; } - public override IEnumerable GetDynamicMemberNames() + public override IEnumerable GetDynamicMemberNames(JObject instance) { - return Value.Properties().Select(p => p.Name); + return instance.Properties().Select(p => p.Name); } } #endif diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 796d58b5..3d323006 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -1283,7 +1283,7 @@ namespace Newtonsoft.Json.Linq /// protected virtual DynamicMetaObject GetMetaObject(Expression parameter) { - return new DynamicProxyMetaObject(parameter, new DynamicProxy(this), true); + return new DynamicProxyMetaObject(parameter, this, new DynamicProxy(), true); } /// diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 293749f4..68ec2830 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -29,6 +29,11 @@ using System.Linq; using System.Text; using Newtonsoft.Json.Utilities; using System.Globalization; +using System.ComponentModel; +#if !(NET35 || NET20 || SILVERLIGHT) +using System.Dynamic; +using System.Linq.Expressions; +#endif namespace Newtonsoft.Json.Linq { @@ -335,13 +340,13 @@ namespace Newtonsoft.Json.Linq internal override int GetDeepHashCode() { int valueHashCode = (_value != null) ? _value.GetHashCode() : 0; - + return _valueType.GetHashCode() ^ valueHashCode; } private static bool ValuesEquals(JValue v1, JValue v2) { - return (v1 == v2|| (v1._valueType == v2._valueType && Compare(v1._valueType, v1._value, v2._value))); + return (v1 == v2 || (v1._valueType == v2._valueType && Compare(v1._valueType, v1._value, v2._value))); } /// @@ -394,5 +399,51 @@ namespace Newtonsoft.Json.Linq return _value.GetHashCode(); } + +#if !(NET35 || NET20 || SILVERLIGHT) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected override DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, this, new JValueDynamicProxy(), true); + } + + private class JValueDynamicProxy : DynamicProxy + { + public override bool TryConvert(JValue instance, ConvertBinder binder, out object result) + { + if (binder.Type == typeof(JValue)) + { + result = instance; + return true; + } + + object value = instance.Value; + + if (value == null) + { + result = null; + return ReflectionUtils.IsNullable(binder.Type); + } + + Type t = binder.Type; + if (ReflectionUtils.IsNullableType(t)) + t = Nullable.GetUnderlyingType(t); + + TypeConverter converter = TypeDescriptor.GetConverter(instance.Value); + if (converter != null && converter.CanConvertTo(t)) + result = converter.ConvertTo(instance.Value, t); + else + result = Convert.ChangeType(instance.Value, t, CultureInfo.InvariantCulture); + + return true; + } + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs b/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs index cadbe938..9133bd09 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs @@ -10,81 +10,74 @@ namespace Newtonsoft.Json.Utilities { internal class DynamicProxy { - public T Value { get; private set; } - - public DynamicProxy(T value) - { - Value = value; - } - - public virtual IEnumerable GetDynamicMemberNames() + public virtual IEnumerable GetDynamicMemberNames(T instance) { return new string[0]; } - public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) + public virtual bool TryBinaryOperation(T instance, BinaryOperationBinder binder, object arg, out object result) { result = null; return false; } - public virtual bool TryConvert(ConvertBinder binder, out object result) + public virtual bool TryConvert(T instance, ConvertBinder binder, out object result) { result = null; return false; } - public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result) + public virtual bool TryCreateInstance(T instance, CreateInstanceBinder binder, object[] args, out object result) { result = null; return false; } - public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes) + public virtual bool TryDeleteIndex(T instance, DeleteIndexBinder binder, object[] indexes) { return false; } - public virtual bool TryDeleteMember(DeleteMemberBinder binder) + public virtual bool TryDeleteMember(T instance, DeleteMemberBinder binder) { return false; } - public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + public virtual bool TryGetIndex(T instance, GetIndexBinder binder, object[] indexes, out object result) { result = null; return false; } - public virtual bool TryGetMember(GetMemberBinder binder, out object result) + public virtual bool TryGetMember(T instance, GetMemberBinder binder, out object result) { result = null; return false; } - public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result) + public virtual bool TryInvoke(T instance, InvokeBinder binder, object[] args, out object result) { result = null; return false; } - public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + public virtual bool TryInvokeMember(T instance, InvokeMemberBinder binder, object[] args, out object result) { result = null; return false; } - public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + public virtual bool TrySetIndex(T instance, SetIndexBinder binder, object[] indexes, object value) { return false; } - public virtual bool TrySetMember(SetMemberBinder binder, object value) + public virtual bool TrySetMember(T instance, SetMemberBinder binder, object value) { return false; } - public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result) + public virtual bool TryUnaryOperation(T instance, UnaryOperationBinder binder, out object result) { result = null; return false; diff --git a/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs index 58ba434f..98111989 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs @@ -13,8 +13,8 @@ namespace Newtonsoft.Json.Utilities private readonly DynamicProxy _proxy; private readonly bool _dontFallbackFirst; - internal DynamicProxyMetaObject(Expression expression, DynamicProxy proxy, bool dontFallbackFirst) - : base(expression, BindingRestrictions.Empty, proxy.Value) + internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy proxy, bool dontFallbackFirst) + : base(expression, BindingRestrictions.Empty, value) { _proxy = proxy; _dontFallbackFirst = dontFallbackFirst; @@ -218,11 +218,11 @@ namespace Newtonsoft.Json.Utilities // ParameterExpression result = Expression.Parameter(typeof(object), null); - - Expression[] callArgs = new Expression[args.Length + 2]; - Array.Copy(args, 0, callArgs, 1, args.Length); - callArgs[0] = Constant(binder); - callArgs[callArgs.Length - 1] = result; + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof(T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + callArgs.Add(result); DynamicMetaObject resultMO = new DynamicMetaObject(result, BindingRestrictions.Empty); @@ -279,8 +279,12 @@ namespace Newtonsoft.Json.Utilities // } // ParameterExpression result = Expression.Parameter(typeof(object), null); - Expression[] callArgs = AddFirst(args, Constant(binder)); - callArgs[args.Length] = Expression.Assign(result, callArgs[args.Length]); + + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof (T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + callArgs[args.Length + 1] = Expression.Assign(result, callArgs[args.Length + 1]); DynamicMetaObject callDynamic = new DynamicMetaObject( Expression.Block( @@ -310,14 +314,6 @@ namespace Newtonsoft.Json.Utilities return _dontFallbackFirst ? callDynamic : fallback(callDynamic); } - internal static TItem[] AddFirst(IList list, TItem item) - { - TItem[] res = new TItem[list.Count + 1]; - res[0] = item; - list.CopyTo(res, 1); - return res; - } - /// /// Helper method for generating a MetaObject which calls a /// specific method on Dynamic, but uses one of the arguments for @@ -331,6 +327,11 @@ namespace Newtonsoft.Json.Utilities // DynamicMetaObject fallbackResult = fallback(null); + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof(T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + // // Build a new expression like: // if (TryDeleteMember(payload)) { } else { fallbackResult } @@ -340,7 +341,7 @@ namespace Newtonsoft.Json.Utilities Expression.Call( Expression.Constant(_proxy), typeof(DynamicProxy).GetMethod(methodName), - AddFirst(args, Constant(binder)) + callArgs ), Expression.Empty(), fallbackResult.Expression, @@ -366,15 +367,14 @@ namespace Newtonsoft.Json.Utilities /// private BindingRestrictions GetRestrictions() { - // ReSharper disable CompareNonConstrainedGenericWithNull - return Value == null && HasValue // ReSharper restore CompareNonConstrainedGenericWithNull + return (Value == null && HasValue) ? BindingRestrictions.GetInstanceRestriction(Expression, null) : BindingRestrictions.GetTypeRestriction(Expression, LimitType); } public override IEnumerable GetDynamicMemberNames() { - return _proxy.GetDynamicMemberNames(); + return _proxy.GetDynamicMemberNames(Value); } // It is okay to throw NotSupported from this binder. This object