从JToken获取可能不存在的价值(最佳做法)


117

使用Json.NET检索C#中甚至可能不存在的JSON值的最佳实践是什么?

现在,我正在处理一个JSON提供程序,该提供程序返回的JSON有时包含某些键/值对,有时不包含。我一直在使用(可能不正确)此方法来获取我的值(例如获取双精度值的示例):

if(null != jToken["width"])
    width = double.Parse(jToken["width"].ToString());
else
    width = 100;

现在可以正常工作,但是当它们很多时很麻烦。我最终写了一个扩展方法,只有写完它之后,我才想知道我是否是愚蠢的……无论如何,这是扩展方法(我只包含双精度和字符串的情况,但实际上我有很多更多):

public static T GetValue<T>(this JToken jToken, string key,
                            T defaultValue = default(T))
{
    T returnValue = defaultValue;

    if (jToken[key] != null)
    {
        object data = null;
        string sData = jToken[key].ToString();

        Type type = typeof(T);

        if (type is double)
            data = double.Parse(sData);
        else if (type is string)
            data = sData;

        if (null == data && type.IsValueType)
            throw new ArgumentException("Cannot parse type \"" + 
                type.FullName + "\" from value \"" + sData + "\"");

        returnValue = (T)Convert.ChangeType(data, 
            type, CultureInfo.InvariantCulture);
    }

    return returnValue;
}

这是使用扩展方法的示例:

width = jToken.GetValue<double>("width", 100);

顺便说一句,请原谅什么可能是一个非常愚蠢的问题,因为它看起来似乎应该有一个内置的函数...我曾尝试谷歌,Json.NET文档,但是我现在不是无能在寻找解决我的问题还是文档中不清楚。


我知道这是有点晚了,但你可能要尝试这个简化版GetValue如下
LB

Answers:


210

这几乎是通用方法Value()的用途。如果将其与可为空的值类型和??运算符结合使用,则将获得所需的行为:

width = jToken.Value<double?>("width") ?? 100;

4
这是一种扩展方法。
Dave Van den Eynde

2
@PaulHazen,还不错...您只是重新发明了轮子而已。
devinbost '16

如果“宽度”不会在JSON存在,JToken为空,这并不工作
迪派

2
@Deepak如果“宽度”不存在,它将起作用。当然,如果jTokenis是行不通的null,那不是问题要问的。而且,您可以使用null条件运算符轻松解决此问题width = jToken?.Value<double?>("width") ?? 100;
svick

1
JToken.Value<T>如果JToken是JValue,则引发异常
Kyle Delaney

22

我会写GetValue如下

public static T GetValue<T>(this JToken jToken, string key, T defaultValue = default(T))
{
    dynamic ret = jToken[key];
    if (ret == null) return defaultValue;
    if (ret is JObject) return JsonConvert.DeserializeObject<T>(ret.ToString());
    return (T)ret;
}

这样,您不仅可以获取基本类型的值,还可以获取复杂对象的值。这是一个样本

public class ClassA
{
    public int I;
    public double D;
    public ClassB ClassB;
}
public class ClassB
{
    public int I;
    public string S;
}

var jt = JToken.Parse("{ I:1, D:3.5, ClassB:{I:2, S:'test'} }");

int i1 = jt.GetValue<int>("I");
double d1 = jt.GetValue<double>("D");
ClassB b = jt.GetValue<ClassB>("ClassB");

那很酷,但是我喜欢关注的分离,只有简单的数据类型才能给我带来好处。尽管在JSON解析时,这种分离的概念有些模糊。因为我实现了一个观察者/可观察模型(也使用mvvm),所以我倾向于将所有解析都放在一个地方,并保持简单(部分原因还在于返回给我的数据的不可预测性)。
Paul Hazen 2012年

@PaulHazen我不能说我了解你。您的问题是retrieving JSON values that may not even exist,我建议的只是更改您的GetValue方法。我认为它可以作为你想怎么
LB

希望这次我可以更清楚一点。您的方法效果很好,并且可以完全满足我的要求。但是,我的问题中没有解释的更大的上下文是,我正在处理的特定代码是我希望高度可移植的代码。尽管您的方法有争议,但它引入了从GetValue <T>反序列化对象的功能,为避免将代码移至具有更好的JSON解析器的平台,我想避免这种模式(例如,例如Win8)。因此,对于我所问的,是的,您的代码将是完美的。
保罗·哈曾

9

这是检查令牌是否存在的方法:

if (jobject["Result"].SelectToken("Items") != null) { ... }

它检查“结果”中是否存在“项目”。

这是一个导致异常的无效示例:

if (jobject["Result"]["Items"] != null) { ... }

3

您可以简单地进行类型转换,它将为您完成转换,例如

var with = (double?) jToken[key] ?? 100;

null如果该键不存在于对象中,它将自动返回,因此无需对其进行测试。


1

TYPE variable = jsonbody["key"]?.Value<TYPE>() ?? DEFAULT_VALUE;

例如

bool attachMap = jsonbody["map"]?.Value<bool>() ?? false;


1

这照顾空

var body = JObject.Parse("anyjsonString");

body?.SelectToken("path-string-prop")?.ToString();

body?.SelectToken("path-double-prop")?.ToObject<double>();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.