2014-01-28 36 views
4

Using Json.NET,我看到所有本地类型的转化的JToken是隐含的,but conversions JToken是明确的。从JToken隐式转换在Json.NET

我的动机是为了避免if报表的显式转换,方法调用等。例如,它本来不错,如果最后if没有抛出:

string dummyJson = @"{'ShouldDoStuff': true}"; 
dynamic parsed = JValue.Parse(dummyJson); 

// Works: 
bool explicitShouldDoStuff = parsed.ShouldDoStuff; 

// Also works: 
if ((bool)parsed.ShouldDoStuff) 
    Console.WriteLine("Hooray, there's a rosebush?"); 

// Throws RuntimeBinderException: Cannot implicitly convert type 'Newtonsoft.Json.Linq.JValue' to 'bool' 
if (parsed.ShouldDoStuff) 
    Console.WriteLine("Die you gravy-sucking pigs"); 

有没有一种方法,使转换从JToken到隐含的本地类型?

+0

问题在哪里? – meilke

+0

@meilke - 改写了这个问题 – bavaza

+1

也许你应该考虑反序列化成强类型类而不是使用'dynamic'。 –

回答

4

显式操作符的原因是隐式操作符会导致各种问题。所以,不,你不能这样做,这是设计。

然而,除了明确的转换,还可以得到Value属性:

if (parsed.ShouldDoStuff.Value) 
    Console.WriteLine("Die you gravy-sucking pigs"); 

我觉得它比类型转换更清洁。

+0

你能举一个例子来说明使用隐式转换操作符导致的问题吗? – bavaza

+1

一些文章:http://netvignettes.wordpress.com/2011/04/24/implicit-conversion-operators-are-bad/,http://blogs.msdn.com/b/oldnewthing/archive/2006/ 5月24日/ 605974.aspx – Athari

1

如果你不介意让你的双手肮脏的动态元对象工作,你可以编写一个包装类,可以在后台将值转换为bool

包装的所有执行的第一:

public class JTokenWrapper : DynamicObject 
{ 
    public JTokenWrapper(JToken token) 
    { 
     if (token == null) throw new ArgumentNullException("token"); 
     this.Token = token; 
    } 
    public JToken Token { get; private set; } 

    public override DynamicMetaObject GetMetaObject(Expression parameter) 
    { 
     return new JValueUnwrapperMetaObject(parameter, Token); 
    } 

    class JValueUnwrapperMetaObject : ProjectedDynamicMetaObjectWrapper<JToken> 
    { 
     public JValueUnwrapperMetaObject(Expression expression, JToken token) 
      : base(expression, ExpressionSelector, token) 
     { 
     } 

     private static Expression ExpressionSelector(Expression expression) 
     { 
      return LinqExpression.Property(
       LinqExpression.Convert(expression, typeof(JTokenWrapper)), 
       "Token" 
      ); 
     } 

     public override DynamicMetaObject BindGetMember(GetMemberBinder binder) 
     { 
      return UnwrappedValue(base.BindGetMember(binder)); 
     } 

     public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) 
     { 
      return UnwrappedValue(base.BindGetIndex(binder, indexes)); 
     } 

     private DynamicMetaObject UnwrappedValue(DynamicMetaObject getter) 
     { 
      var expr = GenerateUnwrapperExpression(getter.Expression); 
      return new DynamicMetaObject(expr, getter.Restrictions, getter.Value); 
     } 

     private static readonly Dictionary<JTokenType, Func<Expression, Expression>> UnwrappedTypes = new Dictionary<JTokenType, Func<Expression, Expression>> 
     { 
      { JTokenType.Boolean, UnwrapBoolean }, 
      { JTokenType.String, UnwrapString }, 
      { JTokenType.Integer, UnwrapInteger }, 
     }; 

     private static Expression ExplicitConvert(Expression token, Type type) 
     { 
      return LinqExpression.Convert(
       token, 
       type, 
       typeof(JToken).GetMethods().Where(m => m.Name == "op_Explicit").Single(m => m.ReturnType == type) 
      ); 
     } 

     private static Expression UnwrapBoolean(Expression token) 
     { 
      return ExplicitConvert(token, typeof(bool)); 
     } 

     private static Expression UnwrapString(Expression token) 
     { 
      return ExplicitConvert(token, typeof(string)); 
     } 

     private static Expression UnwrapInteger(Expression token) 
     { 
      // TODO: figure out the appropriate type 
      return token; 
     } 

     private Expression GenerateUnwrapperExpression(Expression value) 
     { 
      var token = LinqExpression.Variable(typeof(JToken)); 
      var returnTarget = LinqExpression.Label(typeof(object)); 
      return LinqExpression.Block(
       typeof(object), 
       new ParameterExpression[] { token }, 
       LinqExpression.Assign(token, LinqExpression.Convert(value, typeof(JToken))), 
       LinqExpression.Switch(
        LinqExpression.Property(token, "Type"), 
        UnwrappedTypes.Select(x => 
         LinqExpression.SwitchCase(
          LinqExpression.Return(returnTarget, 
           LinqExpression.Convert(
            x.Value(token), 
            typeof(object) 
           ) 
          ), 
          LinqExpression.Constant(x.Key) 
         ) 
        ).ToArray() 
       ), 
       LinqExpression.Label(
        returnTarget, 
        LinqExpression.New(
         typeof(JTokenWrapper).GetConstructors().Single(), 
         LinqExpression.Convert(value, typeof(JToken)) 
        ) 
       ) 
      ); 
     } 
    } 
} 

而且支持类需要:

public abstract class DynamicMetaObjectWrapper : DynamicMetaObject 
{ 
    public DynamicMetaObjectWrapper(Expression expression, DynamicMetaObject wrappedMetaObject) 
     : base(expression, wrappedMetaObject.Restrictions, wrappedMetaObject.Value) 
    { 
     this.MetaObject = wrappedMetaObject; 
    } 
    public DynamicMetaObjectWrapper(DynamicMetaObject wrappedMetaObject) 
     : this(wrappedMetaObject.Expression, wrappedMetaObject) 
    { 
    } 
    public DynamicMetaObject MetaObject { get; private set; } 

    public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) 
    { 
     return MetaObject.BindBinaryOperation(binder, arg); 
    } 

    public override DynamicMetaObject BindConvert(ConvertBinder binder) 
    { 
     return MetaObject.BindConvert(binder); 
    } 
    public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) 
    { 
     return MetaObject.BindCreateInstance(binder, args); 
    } 

    public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) 
    { 
     return MetaObject.BindDeleteIndex(binder, indexes); 
    } 

    public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) 
    { 
     return MetaObject.BindDeleteMember(binder); 
    } 

    public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) 
    { 
     return MetaObject.BindGetIndex(binder, indexes); 
    } 

    public override DynamicMetaObject BindGetMember(GetMemberBinder binder) 
    { 
     return MetaObject.BindGetMember(binder); 
    } 

    public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) 
    { 
     return MetaObject.BindInvoke(binder, args); 
    } 

    public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) 
    { 
     return MetaObject.BindInvokeMember(binder, args); 
    } 

    public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) 
    { 
     return MetaObject.BindSetIndex(binder, indexes, value); 
    } 

    public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) 
    { 
     return MetaObject.BindSetMember(binder, value); 
    } 

    public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) 
    { 
     return MetaObject.BindUnaryOperation(binder); 
    } 

    public override IEnumerable<string> GetDynamicMemberNames() 
    { 
     return MetaObject.GetDynamicMemberNames(); 
    } 
} 
public abstract class ProjectedDynamicMetaObjectWrapper<TProvider> : DynamicMetaObjectWrapper where TProvider : class, IDynamicMetaObjectProvider 
{ 
    public ProjectedDynamicMetaObjectWrapper(Expression expression, Func<Expression, Expression> expressionSelector, TProvider provider) 
     : base(expression, provider.GetMetaObject(expressionSelector(expression))) 
    { 
    } 
} 

然后,所有你需要做的就是用这个包装包裹令牌:

var jsonStr = @"{""ShouldDoStuff"":true}"; 
var value = JValue.Parse(jsonStr); 
dynamic wrapped = new JTokenWrapper(value); 

if (wrapped.ShouldDoStuff) 
{ 
    // success! 
}