【问题标题】:issue/bug using SelectTokens with JsonPath使用带有 JsonPath 的 SelectTokens 的问题/错误
【发布时间】:2016-03-16 16:33:36
【问题描述】:

我在使用带有特定 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()有bug吗?

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

我希望有人能对此有所启发。

更新: 认为这个问题是在我们尝试使用“null”值时发生的,但无论我运行什么不等式查询,JSON 都不会返回 {} / []。

【问题讨论】:

    标签: c# json json.net jsonpath


    【解决方案1】:

    我可以让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" 的属性的所有对象,该属性存在且具有非空值,无论该值是原始容器还是嵌套容器。

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

    您获得所需物品的选择包括:

    1. 您当然可以report an issue 讨论这个问题。 Newtonsoft 可以确定当前行为是错误的,并进行更改。

    2. 您可以 fork 自己的 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;
          }
      }
      

    最后,你问我不明白为什么它在 javascript 中可以正常工作,但在 Json.Net 的 selectTokens 中却不行。。 Json.NET 的行为与http://jsonpath.com/ 不同的原因是:

    1. 关于script expression 究竟是什么,标准完全模棱两可,而且

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

      一个名为“value”的 JValue 类型的值,它不等于 null json 原语。

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

      一个名为“value”的值,它不等于null json 原语。

      与 javascript 相比,c# 是强类型的这一事实可以解释这种差异。

    【讨论】:

    • 感谢您的回答!抱歉,但应该提到 "$.[?(@.value!=null)]" 的全部目的是只返回键 "value" 具有值的对象。挑战是避免返回即。 **{"name":"testnull","value":null}。关于在线的 JSONPath 测试,我不明白为什么会有一些差异。在您提到的网站上进行测试,但是我确实注意到了错误;在 www.jsonpath.com 或 www.jsonquerytool.com 上,它与“$.[?(@.value!=null)]”一起使用。我不明白为什么它在 javascript 中可以正常工作,但在 Json.Net 的 selectTokens 中却不行。关于修复的任何想法?
    • 很好的答案!感谢您的澄清。将报告为问题并开始调查选项 2,因为我不希望使用 LINQ 对“值”键进行静态检查。可能是其他 JSON 中键的另一个名称。
    • 更新:嘿,今天我注意到我被误导了。对象/数组的返回不取决于 JsonPath $.[?(@.value!=null)] - 你运行什么不等式查询并不重要。例如,我测试了 $.[?(@.value!='string')] 这也不会返回对象/数组。
    【解决方案2】:

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

    这是你需要的:

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

    【讨论】:

    • 我认为您误解了问题和问题。我不是在检查空值,而是避免使用 JsonPath 返回对象键“值”可能为空的潜在标记。该问题已被识别并报告给 Newtonsoft。
    • 是的,我的错。我在浏览器中进行了尝试,并没有费心去验证 Json.NET 是否与行为匹配。但是,我的观点仍然是关于 '!=null' 仍然是不必要的——如果/当错误被修复时,我发布的 Jsonpath 应该会给你你想要的结果。
    【解决方案3】:

    终于弄清楚是什么导致 .SelectTokens 在使用 JSONPath 时忽略数组和对象。问题出现在QueryExpressions.cs line 78

    JValue v = r as JValue;
    

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

                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。请随时评论解决方案或建议替代和更好的方法。

    更新:很高兴通知新版本已修复此问题。请参阅GITHUB 了解更多信息。

    【讨论】:

    猜你喜欢
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 2020-12-18
    • 2019-03-29
    相关资源
    最近更新 更多