【问题标题】:Implicit conversion from JToken in Json.NET在 Json.NET 中从 JToken 隐式转换
【发布时间】:2014-01-28 09:39:26
【问题描述】:

使用Json.NET,我看到所有原生类型到 JToken 的转换都是隐式的,但是from JToken 的转换是显式的。

我的动机是避免在 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 隐式转换为原生类型?

【问题讨论】:

  • 问题在哪里?
  • @meilke - 重新表述了问题
  • 也许你应该考虑反序列化为强类型类而不是使用dynamic。
  • 您始终可以选择为JToken 创建一个动态包装器,该包装器将使用最合适的类型返回属性。但这可能比它的价值更麻烦。不幸的是,动态实现并不像它可能的那样健壮。

标签: c# type-conversion json.net


【解决方案1】:

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

不过,除了显式转换,还可以获取Value属性:

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

我认为它比类型转换更干净。

【讨论】:

【解决方案2】:

如果您不介意处理动态元对象,您可以编写一个包装类,在后台将值转换为 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!
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    相关资源
    最近更新 更多