-Make .NET 4.0 dynamic actually work (oops)
-Added JValue dynamic value conversion
This commit is contained in:
@@ -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<bool>(true);
|
||||
AssertValueConverted<bool?>(true);
|
||||
AssertValueConverted<bool?>(false);
|
||||
AssertValueConverted<byte[]>(null);
|
||||
AssertValueConverted<byte[]>(Encoding.UTF8.GetBytes("blah"));
|
||||
AssertValueConverted<DateTime>(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc));
|
||||
AssertValueConverted<DateTime?>(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc));
|
||||
AssertValueConverted<DateTime?>(null);
|
||||
AssertValueConverted<DateTimeOffset>(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1)));
|
||||
AssertValueConverted<DateTimeOffset?>(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1)));
|
||||
AssertValueConverted<DateTimeOffset?>(null);
|
||||
AssertValueConverted<decimal>(99.9m);
|
||||
AssertValueConverted<decimal?>(99.9m);
|
||||
AssertValueConverted<double>(99.9);
|
||||
@@ -150,6 +166,7 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
AssertValueConverted<ulong?>(ulong.MaxValue);
|
||||
AssertValueConverted<ushort>(ushort.MinValue);
|
||||
AssertValueConverted<ushort?>(ushort.MinValue);
|
||||
AssertValueConverted<ushort?>(null);
|
||||
}
|
||||
|
||||
private static void AssertValueConverted<T>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +267,6 @@
|
||||
<Compile Include="Utilities\ReflectionUtilsTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.Config" />
|
||||
<None Include="LinqToSql\LinqToSqlClasses.dbml">
|
||||
<Generator>MSLinqToSQLGenerator</Generator>
|
||||
<LastGenOutput>LinqToSqlClasses.designer.cs</LastGenOutput>
|
||||
|
||||
@@ -3827,63 +3827,5 @@ keyword such as type of business.""
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
[Test]
|
||||
public void SerializingIEnumerableOfTShouldRetainGenericTypeInfo()
|
||||
{
|
||||
CustomEnumerable<Product> products = new CustomEnumerable<Product>();
|
||||
|
||||
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<T> : IEnumerable<T>
|
||||
{
|
||||
//NOTE: a simple linked list
|
||||
private readonly T value;
|
||||
private readonly CustomEnumerable<T> next;
|
||||
private readonly int count;
|
||||
|
||||
private CustomEnumerable(T value, CustomEnumerable<T> next)
|
||||
{
|
||||
this.value = value;
|
||||
this.next = next;
|
||||
count = this.next.count + 1;
|
||||
}
|
||||
|
||||
public CustomEnumerable()
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
|
||||
public CustomEnumerable<T> AddFirst(T newVal)
|
||||
{
|
||||
return new CustomEnumerable<T>(newVal, this);
|
||||
}
|
||||
|
||||
public IEnumerator<T> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<UrlStatus> statues = (List<UrlStatus>) newCollection["List"];
|
||||
Assert.AreEqual(2, statues.Count);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void SerializingIEnumerableOfTShouldRetainGenericTypeInfo()
|
||||
{
|
||||
string productClassRef = ReflectionUtils.GetTypeName(typeof(Product[]), FormatterAssemblyStyle.Simple);
|
||||
|
||||
CustomEnumerable<Product> products = new CustomEnumerable<Product>();
|
||||
|
||||
string json = JsonConvert.SerializeObject(products, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
|
||||
|
||||
Assert.AreEqual(@"{
|
||||
""$type"": """ + productClassRef + @""",
|
||||
""$values"": []
|
||||
}", json);
|
||||
}
|
||||
|
||||
public class CustomEnumerable<T> : IEnumerable<T>
|
||||
{
|
||||
//NOTE: a simple linked list
|
||||
private readonly T value;
|
||||
private readonly CustomEnumerable<T> next;
|
||||
private readonly int count;
|
||||
|
||||
private CustomEnumerable(T value, CustomEnumerable<T> next)
|
||||
{
|
||||
this.value = value;
|
||||
this.next = next;
|
||||
count = this.next.count + 1;
|
||||
}
|
||||
|
||||
public CustomEnumerable()
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
|
||||
public CustomEnumerable<T> AddFirst(T newVal)
|
||||
{
|
||||
return new CustomEnumerable<T>(newVal, this);
|
||||
}
|
||||
|
||||
public IEnumerator<T> 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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -666,23 +666,19 @@ namespace Newtonsoft.Json.Linq
|
||||
/// </returns>
|
||||
protected override DynamicMetaObject GetMetaObject(Expression parameter)
|
||||
{
|
||||
return new DynamicProxyMetaObject<JObject>(parameter, new JObjectDynamicProxy(this), true);
|
||||
return new DynamicProxyMetaObject<JObject>(parameter, this, new JObjectDynamicProxy(), true);
|
||||
}
|
||||
|
||||
private class JObjectDynamicProxy : DynamicProxy<JObject>
|
||||
{
|
||||
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<string> GetDynamicMemberNames()
|
||||
public override IEnumerable<string> GetDynamicMemberNames(JObject instance)
|
||||
{
|
||||
return Value.Properties().Select(p => p.Name);
|
||||
return instance.Properties().Select(p => p.Name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1283,7 +1283,7 @@ namespace Newtonsoft.Json.Linq
|
||||
/// </returns>
|
||||
protected virtual DynamicMetaObject GetMetaObject(Expression parameter)
|
||||
{
|
||||
return new DynamicProxyMetaObject<JToken>(parameter, new DynamicProxy<JToken>(this), true);
|
||||
return new DynamicProxyMetaObject<JToken>(parameter, this, new DynamicProxy<JToken>(), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -394,5 +399,51 @@ namespace Newtonsoft.Json.Linq
|
||||
|
||||
return _value.GetHashCode();
|
||||
}
|
||||
|
||||
#if !(NET35 || NET20 || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> responsible for binding operations performed on this object.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The expression tree representation of the runtime value.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="T:System.Dynamic.DynamicMetaObject"/> to bind this object.
|
||||
/// </returns>
|
||||
protected override DynamicMetaObject GetMetaObject(Expression parameter)
|
||||
{
|
||||
return new DynamicProxyMetaObject<JValue>(parameter, this, new JValueDynamicProxy(), true);
|
||||
}
|
||||
|
||||
private class JValueDynamicProxy : DynamicProxy<JValue>
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -10,81 +10,74 @@ namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
internal class DynamicProxy<T>
|
||||
{
|
||||
public T Value { get; private set; }
|
||||
|
||||
public DynamicProxy(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> GetDynamicMemberNames()
|
||||
public virtual IEnumerable<string> 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;
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Newtonsoft.Json.Utilities
|
||||
private readonly DynamicProxy<T> _proxy;
|
||||
private readonly bool _dontFallbackFirst;
|
||||
|
||||
internal DynamicProxyMetaObject(Expression expression, DynamicProxy<T> proxy, bool dontFallbackFirst)
|
||||
: base(expression, BindingRestrictions.Empty, proxy.Value)
|
||||
internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy<T> 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<Expression> callArgs = new List<Expression>();
|
||||
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<Expression> callArgs = new List<Expression>();
|
||||
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<TItem>(IList<TItem> list, TItem item)
|
||||
{
|
||||
TItem[] res = new TItem[list.Count + 1];
|
||||
res[0] = item;
|
||||
list.CopyTo(res, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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<Expression> callArgs = new List<Expression>();
|
||||
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<T>).GetMethod(methodName),
|
||||
AddFirst(args, Constant(binder))
|
||||
callArgs
|
||||
),
|
||||
Expression.Empty(),
|
||||
fallbackResult.Expression,
|
||||
@@ -366,15 +367,14 @@ namespace Newtonsoft.Json.Utilities
|
||||
/// </summary>
|
||||
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<string> GetDynamicMemberNames()
|
||||
{
|
||||
return _proxy.GetDynamicMemberNames();
|
||||
return _proxy.GetDynamicMemberNames(Value);
|
||||
}
|
||||
|
||||
// It is okay to throw NotSupported from this binder. This object
|
||||
|
||||
Reference in New Issue
Block a user