我在使用NewtonSoft v8.0.3 JToken.SelectTokens()和特定的JsonPath时遇到问题。要在 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/工作正常,请参阅此工作小提琴
我希望有人能对此有所了解。
更新:认为当我们尝试使用"null"值时发生了此问题,但是无论我运行什么不等式查询,都不会从 JSON 返回 {}/[]。
我可以通过使用存在运算符而不是不等式运算符来SelectTokens()
返回您想要的结果:
string path = "$.[?(@.value)]";
现在返回所有 4 个对象。 样品小提琴。
至于这是否是一个错误,JSONPath的文章是模棱两可的。 它说:
[] ?() applies a filter (script) expression. n/a () script expression, using the underlying script engine.
但是,使用底层脚本引擎实际上意味着什么? 如果我在 https://jsonpath.curiousconcept.com/测试您的 JSON 和查询表达式,则会为 Flow Communications JSONPath 0.3.1
和Stefan Goessner JSONPath 0.8.3
生成错误。 如果我使用我的存在查询,那么前者可以正常工作,但后者仍然会引发异常。
Json.NET 应该像现在这样行事吗? 此实现隐式提供了筛选具有基元值的属性的功能。 建议的行为将失去此功能。 也就是说,任何对象或数组值总是匹配任何不等式运算符,例如 "$.[?(@.value!='aString')]"
,这可能是意料之外的。
更新
阐明的要求是运行查询"$.[?(@.value!=null)]"
以返回具有名为 "value"
的属性的所有对象,该属性存在且具有非 null 值,无论该值是基元还是嵌套容器。
一方面,这似乎是一个合理的查询,应该是可能的。 另一方面,当前能够在不获取所有容器值的情况下对基元值运行不等式查询的功能似乎很有用,也应该是可能的。 对该特定查询字符串的任何解释似乎都是合理的;JSONPath 语言的定义太模糊了,给实现者留下了太多的解释。
获取所需内容的选项包括:
您当然可以报告有关此的问题。 Newtonsoft可以决定当前的行为是错误的,并改变它。
您可以分叉自己的 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;
您可以使用以下解决方法:
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/不同的原因是:
该标准对于
script expression
到底是什么完全模棱两可,并且它们是使用不同的技术和代码库实现的。 Json.NET
(@.value!=null)
解释为含义名为"value"的类型
JValue
值,不等于null
JSON 基元。而另一个解析器将查询解释为
名为"value"的值,不等于
null
JSON 基元。与javascript相比,c#是强类型的这一事实可以解释其中的区别。
Jsonpath 没有标准的实现,因此您的结果会因您使用的解析器而异。 但是,您永远不需要检查空值,因为它对 Jsonpath 没有真正的意义。
这是您需要的:
var result = jt.SelectTokens("$.[?(@.value)]");
终于弄清楚了导致 .选择令牌以在使用 JSONPath 时忽略数组和对象。问题出现在查询表达式中.cs第 78 行
JValue v = r as JValue;
这将导致任何 JObject/JArray 变为空,因此在比较时被忽略。我解决此问题的方法是将第 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;
}
这将确保在调用 时返回 JObject 和 JArray。选择令牌。请随时评论解决方案或建议替代和更好的方法。
更新:很高兴地通知,此问题已在新版本中修复。有关更多信息,请参阅 GITHUB。