使用带有JsonPath的SelectTokens的问题/错误

时间:2016-03-16 16:33:37

标签: c# json json.net jsonpath

我在使用具有特定JsonPath的NewtonSoft v8.0.3 JToken.SelectTokens()时出现问题。要在C#中重现问题:

        string json = "[{\"name\":\"string\",\"value\":\"aString\"},{\"name\":\"number\",\"value\":123},{\"name\":\"array\",\"value\":[1,2,3,4]},{\"name\":\"object\",\"value\":{\"1\":1}}]";
        string path = "$.[?(@.value!=null)]";
        string newJson = String.Empty;

        JToken jt = JToken.Parse(json);
        IEnumerable<JToken> tokens = jt.SelectTokens(path, false);
        newJson = JsonConvert.SerializeObject(tokens, Formatting.Indented);
        // Output is missing objects and arrays??
        // newJson = [{"name": "string","value": "aString"},{"name": "number","value": 123}]

运行上面的代码不会返回任何对象或数组?! Json.Net的JToken.SelectTokens()中是否有错误?

要验证我使用的路径http://goessner.net/articles/JsonPath/,该路径可以正常工作,请参阅此工作fiddle

我希望有人可以对此有所启发。

更新 当我们尝试使用“null”值时发生这个问题,但无论我运行什么不等式查询,都没有从JSON返回{} / []。

3 个答案:

答案 0 :(得分:2)

我可以通过使用存在运算符而不是不等运算符使SelectTokens()返回所需的结果:

string path = "$.[?(@.value)]"; 

现在返回所有4个对象。样本fiddle

至于这是否是一个错误,JSONPath article是不明确的。它说:

[]   ?()     applies a filter (script) expression.
n/a   ()     script expression, using the underlying script engine. 

使用底层脚本引擎实际上意味着什么?如果我在https://jsonpath.curiousconcept.com/测试您的JSON和查询表达式,则会为Flow Communications JSONPath 0.3.1Stefan Goessner JSONPath 0.8.3生成错误。如果我使用我的存在查询,那么前者工作正常但后者仍然抛出异常。

Json.NET应该像现在一样吗?本实现隐式地提供了对具有原始值的属性进行过滤的能力。建议的行为将失去此功能。也就是说,任何对象或数组值总是匹配任何不等的运算符,例如"$.[?(@.value!='aString')]",这可能是不期望的。

<强>更新

您澄清的要求是运行查询"$.[?(@.value!=null)]"以返回所有具有名为"value"的属性的对象,该属性存在且具有非null值,无论该值是基元还是嵌套容器。

一方面,这似乎是一个应该是可能的合理查询。另一方面,当前能够在不获取所有容器值的情况下对原始值运行不等式查询的能力似乎也很有用,并且也应该是可行的。并且对该特定查询字符串的任何解释似乎都是合理的; JSONPath语言定义过于模糊,并且过多地解释了实现者。

您可以选择获得所需内容:

  1. 你当然可以report an issue。 Newtonsoft可以决定当前的行为是错误的,并改变它。

  2. 您可以分叉自己的Json.NET版本并修改BooleanQueryExpression.IsMatch(JToken t),如下所示:

                case QueryOperator.NotEquals:
                    if (v != null && !EqualsWithStringCoercion(v, Value))
                    {
                        return true;
                    }
                    else if (v == null && r != null)
                    {
                        return true;
                    }
                    break;
    
  3. 您可以使用以下解决方法:

    var path = "$.[?(@.value)]";
    var tokens = jt.SelectTokens(path, false)
        .Where(t => !t["value"].IsNull());
    

    使用扩展方法:

    public static class JsonExtensions
    {
        public static bool IsNull(this JToken token)
        {
            return token == null || token.Type == JTokenType.Null;
        }
    }
    
  4. 最后,你问我不明白为什么它在javascript中工作正常,但在Json.Net的selectTokens中却没有。。 Json.NET的行为与http://jsonpath.com/不同的原因是:

    1. 标准对script expression是什么,确切地说是

    2. 完全不明确
    3. 它们是使用不同的技术和代码库实现的。 Json.NET将(@.value!=null)解释为

        

      JValue类型的值,名为&#34; value&#34;这不等于null json原语。

      而另一个解析器将查询解释为

        

      一个名为&#34;值&#34;的值这不等于null json原语。

      与javascript相比,c#强类型的事实可以解释其中的差异。

答案 1 :(得分:0)

最后找出导致.SelectTokens在使用JSONPath时忽略数组和对象的原因。问题出现在QueryExpressions.cs line 78

JValue v = r as JValue;

这会导致任何JObject / JArray的JToken变为 null ,因此在比较时会被忽略。我解决这个问题的方法是用以下处理JObject和JArray的代码替换第78行:

            JValue v = null;
            switch (r.Type)
            {
                case JTokenType.Object:
                case JTokenType.Array:
                    v = new JValue(r.ToString());
                    break;
                default:
                    v = r as JValue;
                    break;
            }

这将确保在调用.SelectTokens时返回JObject和JArray。请随意评论解决方案或建议替代和更好的aproaches。

更新:很高兴知道此问题已通过新版本修复。有关详细信息,请参阅GITHUB

答案 2 :(得分:0)

没有Jsonpath的标准实现,因此您的结果将根据您使用的解析器而有所不同。但是,您永远不需要检查空值,因为它对Jsonpath没有任何意义。

这就是你需要的:

var result = jt.SelectTokens("$.[?(@.value)]");