diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs index 20cb009c..cfc3e74b 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs @@ -63,10 +63,13 @@ namespace Newtonsoft.Json.Tests.Serialization Dictionary values = new Dictionary(); + IContractResolver c = DefaultContractResolver.Instance; + JsonDynamicContract dynamicContract = (JsonDynamicContract)c.ResolveContract(dynamicObject.GetType()); + foreach (string memberName in dynamicObject.GetDynamicMemberNames()) { object value; - dynamicObject.TryGetMember(memberName, out value); + dynamicContract.TryGetMember(dynamicObject, memberName, out value); values.Add(memberName, value); } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index d49942ec..7c6048f1 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -6638,6 +6638,43 @@ Parameter name: value", }", json); } + public class EnumerableClass : IEnumerable + { + private readonly IList _values; + + public EnumerableClass(IEnumerable values) + { + _values = new List(values); + } + + public IEnumerator GetEnumerator() + { + return _values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + [Test] + public void DeserializeIEnumerableFromConstructor() + { + string json = @"[ + ""One"", + ""II"", + ""3"" +]"; + + var result = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(3, result.Count()); + Assert.AreEqual("One", result.ElementAt(0)); + Assert.AreEqual("II", result.ElementAt(1)); + Assert.AreEqual("3", result.ElementAt(2)); + } + [JsonObject(MemberSerialization.Fields)] public class MyTuple { diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs index 49c1abfa..7d8322bc 100644 --- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -974,16 +974,14 @@ namespace Newtonsoft.Json.Converters { manager.PushScope(); - string namespacePrefix = null; - string namespaceUri = null; foreach (IXmlNode attribute in node.Attributes) { if (attribute.NamespaceUri == "http://www.w3.org/2000/xmlns/") { - namespacePrefix = (attribute.LocalName != "xmlns") + string namespacePrefix = (attribute.LocalName != "xmlns") ? attribute.LocalName : string.Empty; - namespaceUri = attribute.Value; + string namespaceUri = attribute.Value; manager.AddNamespace(namespacePrefix, namespaceUri); } @@ -1017,9 +1015,6 @@ namespace Newtonsoft.Json.Converters writer.WriteEndObject(); } - if (namespacePrefix != null) - manager.RemoveNamespace(namespacePrefix, namespaceUri); - manager.PopScope(); } diff --git a/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs index fa7448fb..3eeca1a0 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs @@ -26,7 +26,9 @@ #if !(NET35 || NET20 || WINDOWS_PHONE) using System; using System.Collections.Generic; +using System.Dynamic; using System.Reflection; +using System.Runtime.CompilerServices; using Newtonsoft.Json.Utilities; using System.Collections; @@ -49,6 +51,26 @@ namespace Newtonsoft.Json.Serialization /// The property name resolver. public Func PropertyNameResolver { get; set; } + private readonly ThreadSafeStore>> _callSiteGetters = + new ThreadSafeStore>>(CreateCallSiteGetter); + + private readonly ThreadSafeStore>> _callSiteSetters = + new ThreadSafeStore>>(CreateCallSiteSetter); + + private static CallSite> CreateCallSiteGetter(string name) + { + GetMemberBinder getMemberBinder = (GetMemberBinder)DynamicUtils.BinderWrapper.GetMember(name, typeof(DynamicUtils)); + + return CallSite>.Create(new NoThrowGetBinderMember(getMemberBinder)); + } + + private static CallSite> CreateCallSiteSetter(string name) + { + SetMemberBinder binder = (SetMemberBinder)DynamicUtils.BinderWrapper.SetMember(name, typeof(DynamicUtils)); + + return CallSite>.Create(new NoThrowSetBinderMember(binder)); + } + /// /// Initializes a new instance of the class. /// @@ -60,6 +82,37 @@ namespace Newtonsoft.Json.Serialization Properties = new JsonPropertyCollection(UnderlyingType); } + + internal bool TryGetMember(IDynamicMetaObjectProvider dynamicProvider, string name, out object value) + { + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + + CallSite> callSite = _callSiteGetters.Get(name); + + object result = callSite.Target(callSite, dynamicProvider); + + if (!ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult)) + { + value = result; + return true; + } + else + { + value = null; + return false; + } + } + + internal bool TrySetMember(IDynamicMetaObjectProvider dynamicProvider, string name, object value) + { + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + + CallSite> callSite = _callSiteSetters.Get(name); + + object result = callSite.Target(callSite, dynamicProvider, value); + + return !ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult); + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 1c466cdb..154479cd 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -593,6 +593,10 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { list = (IList) ReflectionUtils.CreateInstance(contract.CreatedType, list); } + else + { + return ReflectionUtils.CreateInstance(contract.CreatedType, list); + } } else if (list is IWrappedCollection) { @@ -610,6 +614,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { value = CreateJToken(reader, contract); } + return value; } @@ -1274,7 +1279,7 @@ To fix this error either change the environment to be fully trusted, change the else value = CreateValueInternal(reader, t, dynamicMemberContract, null, null, member, null); - newObject.TrySetMember(memberName, value); + contract.TrySetMember(newObject, memberName, value); } } catch (Exception ex) diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 63f04e25..a558ca56 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -750,7 +750,7 @@ To fix this error either change the environment to be fully trusted, change the foreach (string memberName in value.GetDynamicMemberNames()) { object memberValue; - if (value.TryGetMember(memberName, out memberValue)) + if (contract.TryGetMember(value, memberName, out memberValue)) { try { diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index f31bc38e..2cc094b3 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -226,6 +226,32 @@ namespace Newtonsoft.Json.Utilities else list = null; } + else if (ReflectionUtils.ImplementsGenericDefinition(listType, typeof (IEnumerable<>), out collectionType)) + { + bool suitableConstructor = false; + + foreach (ConstructorInfo constructor in listType.GetConstructors()) + { + IList parameters = constructor.GetParameters(); + + if (parameters.Count == 1) + { + if (collectionType.IsAssignableFrom(parameters[0].ParameterType)) + { + suitableConstructor = true; + break; + } + } + } + + if (!suitableConstructor) + throw new Exception("{0} does not have a public constructor that takes a type that implements {1}.".FormatWith(CultureInfo.InvariantCulture, listType, collectionType)); + + Type collectionContentsType = collectionType.GetGenericArguments()[0]; + + list = CreateGenericList(collectionContentsType); + isReadOnlyOrFixedSize = true; + } else { list = null; diff --git a/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs index ca6722d2..c2f37df6 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs @@ -85,8 +85,8 @@ namespace Newtonsoft.Json.Utilities for (int i = 0; i < values.Length; i++) { - MethodInfo createArgumentInfoMethod = csharpArgumentInfoType.GetMethod("Create", new[] { csharpArgumentInfoFlags, typeof(string) }); - object arg = createArgumentInfoMethod.Invoke(null, new object[] { 0, null }); + MethodInfo createArgumentInfoMethod = csharpArgumentInfoType.GetMethod("Create", new[] {csharpArgumentInfoFlags, typeof (string)}); + object arg = createArgumentInfoMethod.Invoke(null, new object[] {0, null}); a.SetValue(arg, i); } @@ -99,127 +99,92 @@ namespace Newtonsoft.Json.Utilities Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName); Type binderType = Type.GetType(BinderTypeName); - Type csharpArgumentInfoTypeEnumerableType = typeof(IEnumerable<>).MakeGenericType(csharpArgumentInfoType); + Type csharpArgumentInfoTypeEnumerableType = typeof (IEnumerable<>).MakeGenericType(csharpArgumentInfoType); - MethodInfo getMemberMethod = binderType.GetMethod("GetMember", new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }); + MethodInfo getMemberMethod = binderType.GetMethod("GetMember", new[] {csharpBinderFlagsType, typeof (string), typeof (Type), csharpArgumentInfoTypeEnumerableType}); _getMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(getMemberMethod); - MethodInfo setMemberMethod = binderType.GetMethod("SetMember", new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }); + MethodInfo setMemberMethod = binderType.GetMethod("SetMember", new[] {csharpBinderFlagsType, typeof (string), typeof (Type), csharpArgumentInfoTypeEnumerableType}); _setMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(setMemberMethod); } public static CallSiteBinder GetMember(string name, Type context) { Init(); - return (CallSiteBinder)_getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray); + return (CallSiteBinder) _getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray); } public static CallSiteBinder SetMember(string name, Type context) { Init(); - return (CallSiteBinder)_setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray); + return (CallSiteBinder) _setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray); } } - public static bool TryGetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, out object value) - { - ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); - - GetMemberBinder getMemberBinder = (GetMemberBinder) BinderWrapper.GetMember(name, typeof (DynamicUtils)); - - CallSite> callSite = CallSite>.Create(new NoThrowGetBinderMember(getMemberBinder)); - - object result = callSite.Target(callSite, dynamicProvider); - - if (!ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult)) - { - value = result; - return true; - } - else - { - value = null; - return false; - } - } - - public static bool TrySetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, object value) - { - ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); - - SetMemberBinder binder = (SetMemberBinder)BinderWrapper.SetMember(name, typeof(DynamicUtils)); - - var setterSite = CallSite>.Create(new NoThrowSetBinderMember(binder)); - - object result = setterSite.Target(setterSite, dynamicProvider, value); - - return !ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult); - } - public static IEnumerable GetDynamicMemberNames(this IDynamicMetaObjectProvider dynamicProvider) { DynamicMetaObject metaObject = dynamicProvider.GetMetaObject(Expression.Constant(dynamicProvider)); return metaObject.GetDynamicMemberNames(); } + } - internal class NoThrowGetBinderMember : GetMemberBinder + internal class NoThrowGetBinderMember : GetMemberBinder + { + private readonly GetMemberBinder _innerBinder; + + public NoThrowGetBinderMember(GetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) { - private readonly GetMemberBinder _innerBinder; - - public NoThrowGetBinderMember(GetMemberBinder innerBinder) - : base(innerBinder.Name, innerBinder.IgnoreCase) - { - _innerBinder = innerBinder; - } - - public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) - { - DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { }); - - NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); - Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); - - DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); - return finalMetaObject; - } + _innerBinder = innerBinder; } - internal class NoThrowSetBinderMember : SetMemberBinder + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { - private readonly SetMemberBinder _innerBinder; + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { }); - public NoThrowSetBinderMember(SetMemberBinder innerBinder) - : base(innerBinder.Name, innerBinder.IgnoreCase) - { - _innerBinder = innerBinder; - } + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); - public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) - { - DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { value }); - - NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); - Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); - - DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); - return finalMetaObject; - } - } - - - internal class NoThrowExpressionVisitor : ExpressionVisitor - { - internal static readonly object ErrorResult = new object(); - - protected override Expression VisitConditional(ConditionalExpression node) - { - // if the result of a test is to throw an error, rewrite to result an error result value - if (node.IfFalse.NodeType == ExpressionType.Throw) - return Expression.Condition(node.Test, node.IfTrue, Expression.Constant(ErrorResult)); - - return base.VisitConditional(node); - } + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; } } + + internal class NoThrowSetBinderMember : SetMemberBinder + { + private readonly SetMemberBinder _innerBinder; + + public NoThrowSetBinderMember(SetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) + { + _innerBinder = innerBinder; + } + + public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) + { + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { value }); + + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); + + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; + } + } + + internal class NoThrowExpressionVisitor : ExpressionVisitor + { + internal static readonly object ErrorResult = new object(); + + protected override Expression VisitConditional(ConditionalExpression node) + { + // if the result of a test is to throw an error, rewrite to result an error result value + if (node.IfFalse.NodeType == ExpressionType.Throw) + return Expression.Condition(node.Test, node.IfTrue, Expression.Constant(ErrorResult)); + + return base.VisitConditional(node); + } + } + } #endif \ No newline at end of file