-Make .NET 4.0 dynamic actually work (oops)

-Added JValue dynamic value conversion
This commit is contained in:
JamesNK
2010-11-25 07:11:38 +00:00
parent 5c0431464b
commit efc2310a3d
10 changed files with 179 additions and 117 deletions
+19 -1
View File
@@ -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
+2 -2
View File
@@ -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));
}
}
+7 -11
View File
@@ -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
+1 -1
View File
@@ -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>
+53 -2
View File
@@ -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
}
}
+13 -20
View File
@@ -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