issue/bug 将 SelectTokens 与 JsonPath 结合使用
issue/bug using SelectTokens with 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}]
运行 上面的代码不会 return 任何对象或数组?! Json.Net 的 JToken.SelectTokens() 中是否存在错误?
为了验证我使用的路径 http://goessner.net/articles/JsonPath/ which works fine, see this working fiddle
我希望有人能对此有所启发。
更新:
认为这个问题是在我们尝试使用 "null" 值时发生的,但无论我 运行 是什么不等式查询,都没有 {} / [] 是 return 从 JSON 编辑的。
我可以通过使用存在运算符而不是不等运算符使SelectTokens()
return得到你想要的结果:
string path = "$.[?(@.value)]";
现在所有 4 个对象都已 returned。示例 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.1
和 Stefan Goessner JSONPath 0.8.3
都会产生一个错误。如果我使用我的存在查询,那么前者可以正常工作,但后者仍然会抛出异常。
Json.NET 应该像现在这样吗?当前的实现隐含地提供了过滤具有原始值的属性的能力。提议的行为将 失去 此功能。即,任何对象或数组值都将始终匹配任何不等式运算符,例如 "$.[?(@.value!='aString')]"
,这可能不是预期的。
更新
您明确的要求是 运行 查询 "$.[?(@.value!=null)]"
到 return 所有具有 属性 名为 "value"
且存在且具有非空值的对象值,无论该值是原始容器还是嵌套容器。
一方面,这似乎是一个应该可行的合理查询。另一方面,当前能够 运行 对原始值进行不等式查询而不获取所有容器值的能力似乎很有用,也应该是可能的。并且对该特定查询字符串的任何一种解释似乎都是合理的; JSONPath 语言定义过于模糊,留给实施者的解释太多。
获取所需内容的选项包括:
你当然可以report an issue。 Newtonsoft 可以确定当前行为是错误的,并进行更改。
您可以 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;
您可以使用以下解决方法:
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)
解释为
a value of type JValue
named "value" that is not equal to the null
json primitive.
而另一个解析器将查询解释为
a value named "value" that is not equal to the null
json primitive.
与 javascript 相比,c# 是强类型的这一事实可以解释差异。
终于弄清楚是什么导致 .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。请随时评论解决方案或建议替代和更好的方法。
更新:很高兴地通知新版本已解决此问题。有关详细信息,请参阅 GITHUB。
没有 Jsonpath 的标准实现,因此您的结果会因您使用的解析器而异。但是,您永远不需要检查空值,因为它对 Jsonpath 没有任何意义。
这就是你需要的:
var result = jt.SelectTokens("$.[?(@.value)]");
我在使用具有特定 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}]
运行 上面的代码不会 return 任何对象或数组?! Json.Net 的 JToken.SelectTokens() 中是否存在错误?
为了验证我使用的路径 http://goessner.net/articles/JsonPath/ which works fine, see this working fiddle
我希望有人能对此有所启发。
更新: 认为这个问题是在我们尝试使用 "null" 值时发生的,但无论我 运行 是什么不等式查询,都没有 {} / [] 是 return 从 JSON 编辑的。
我可以通过使用存在运算符而不是不等运算符使SelectTokens()
return得到你想要的结果:
string path = "$.[?(@.value)]";
现在所有 4 个对象都已 returned。示例 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.1
和 Stefan Goessner JSONPath 0.8.3
都会产生一个错误。如果我使用我的存在查询,那么前者可以正常工作,但后者仍然会抛出异常。
Json.NET 应该像现在这样吗?当前的实现隐含地提供了过滤具有原始值的属性的能力。提议的行为将 失去 此功能。即,任何对象或数组值都将始终匹配任何不等式运算符,例如 "$.[?(@.value!='aString')]"
,这可能不是预期的。
更新
您明确的要求是 运行 查询 "$.[?(@.value!=null)]"
到 return 所有具有 属性 名为 "value"
且存在且具有非空值的对象值,无论该值是原始容器还是嵌套容器。
一方面,这似乎是一个应该可行的合理查询。另一方面,当前能够 运行 对原始值进行不等式查询而不获取所有容器值的能力似乎很有用,也应该是可能的。并且对该特定查询字符串的任何一种解释似乎都是合理的; JSONPath 语言定义过于模糊,留给实施者的解释太多。
获取所需内容的选项包括:
你当然可以report an issue。 Newtonsoft 可以确定当前行为是错误的,并进行更改。
您可以 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;
您可以使用以下解决方法:
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)
解释为a value of type
JValue
named "value" that is not equal to thenull
json primitive.而另一个解析器将查询解释为
a value named "value" that is not equal to the
null
json primitive.与 javascript 相比,c# 是强类型的这一事实可以解释差异。
终于弄清楚是什么导致 .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。请随时评论解决方案或建议替代和更好的方法。
更新:很高兴地通知新版本已解决此问题。有关详细信息,请参阅 GITHUB。
没有 Jsonpath 的标准实现,因此您的结果会因您使用的解析器而异。但是,您永远不需要检查空值,因为它对 Jsonpath 没有任何意义。
这就是你需要的:
var result = jt.SelectTokens("$.[?(@.value)]");