2

我正在使用C# 的 JsonPath来查询一些 JSON 数据。JsonPath 没有自带解析器,因此根据Rick Sladkey 的建议,我使用Json.NET将我的 Json 字符串解析为嵌套IDictionary对象、IList数组和原语的集合。然后我使用 JsonPath 对其进行过滤(在添加Rick Sladkey 的答案中建议的类之后)。

作为参考,这是我的代码中实际处理所有这些的部分:

// string exampleJsonString defined below
string query = "$.store.book[*].title" // we should get a list of all titles

// step 1: use Json.NET to parse the json string into a JObject
JObject parsedJson = JObject.Parse(exampleJsonString); 

// step 2: use JsonPath with a custom ValueSystem (JsonNetValueSystem)
JsonPathContext context = new JsonPathContext    
        { ValueSystem = new JsonNetValueSystem() }; 

// step 3: get results using JsonPath
List<object> toRet = context.SelectNodes(parsedJson,
        query).Select(node => node.Value).ToList();

我首先使用 JsonPath 的原因是它的过滤器功能。您不仅可以进行普通查询,例如"$.store.book[*].title"(获取书店中所有书名的数组),还可以进行查询"$.store.book[?(@.category == 'fiction')].title"(获取书店中所有类别匹配“小说”的书名的数组)。我需要能够将整个查询作为字符串传递,因此能够在查询时进行过滤非常有帮助。

不幸的是,我在让这个过滤器功能工作时遇到了一些麻烦。我希望我必须对 JsonNetValueSystem 类(最初在上述堆栈溢出答案中定义)或 JsonPath 命名空间(您可以从JsonPath 的谷歌代码页获取 JsonPath.cs )进行调整。如果有一些外部工具或 Json.NET 的替代解析机制可以让我保持 JsonPath 的过滤而不必编写太多额外的代码,那将是理想的,但我很确定我将不得不改变 JsonNetValueSystem或 JsonPath 本身。(这两个都相当容易更改,因为它们只是 .cs 文件,但如果没有更多工作,我无法真正深入研究 Json.NET。)

我实际上似乎无法弄清楚原始 JsonPath 代码在哪里处理过滤,也无法弄清楚为什么 JsonNetValueSystem 类会剥夺它的功能。任何有关如何在查询字符串中添加过滤功能的建议将不胜感激。即使只是“不要乱用 JsonPath,只需更改 JsonNetValueSystem”,反之亦然。

string exampleJsonString = "{
    "store": {
        "book": [ {
            "category": "reference",
            "author": "Nigel Rees",
            "title": "Sayings of the Century",
            "price": 8.95
        }, {
            "category": "fiction",
            "author": "Evelyn Waugh",
            "title": "Sword of Honour",
            "price": 12.99
        }, {
            "category": "fiction",
            "author": "Herman Melville",
            "title": "Moby Dick",
            "isbn": "0-553-21311-3",
            "price": 8.99
        }, {
            "category": "fiction",
            "author": "J. R. R. Tolkien",
            "title": "The Lord of the Rings",
            "isbn": "0-395-19395-8",
            "price": 22.99
        } ],
        "bicycle": [ {
            "color": "red",
            "price": 19.95,
            "style": [ "city", "hybrid" ]
        }, {
            "color": "blue",
            "price": 59.91,
            "style": [ "downhill", "freeride" ]
        } ]
    }
}"
4

2 回答 2

3

当您在查询(?(...)部分)中使用脚本表达式时,您需要提供一种ScriptEvaluator方法来评估脚本。不幸的是,它们没有为 C# 版本提供默认实现。您需要提供该实现。

开箱即用,这不是要解决的最琐碎的问题,您需要编写一个解释器。您有两个选择:使用 CodeDOM 将脚本编译为 C# 代码(或您喜欢的任何语言),使用 Roslyn 解析脚本并进行评估,只要可行。

对于这种特殊情况,一个快速而肮脏的解决方案是在您的脚本评估器方法中执行类似的操作:

object EvaluateScript(string script, object value, string context)
{
    if (script == "@.category == 'fiction'")
    {
        var obj = value as JObject;
        return (string)obj["category"] == "fiction";
    }
    return null;
}

这是另一个利用 IronPython 评估脚本的解决方案。

public class IronPythonScriptEvaluator
{
    private Lazy<ScriptEngine> engine = new Lazy<ScriptEngine>(() => Python.CreateEngine());
    private ScriptEngine Engine { get { return engine.Value; } }

    private const string ItemName = "_IronPythonScriptEvaluator_item";

    public object EvaluateScript(string script, object value, string context)
    {
        var cleanScript = CleanupScript(script);
        var scope = Engine.CreateScope();
        scope.SetVariable(ItemName, value);
        return Engine.Execute<bool>(cleanScript, scope);
    }

    private string CleanupScript(string script)
    {
        return Regex.Replace(script, @"@", ItemName);
    }
}
于 2013-08-02T20:24:31.087 回答
2

另一种解决方案是改用Manatee.Json。它有一个内置的原生 JSONPath 实现和解析器(连同模式和序列化程序)。此外,您不需要将路径表示为字符串。Manatee.Json 有一个流畅的界面,可用于构建路径,包括表达式支持。

要代表$.store.book[*].title,你会有

var path = JsonPathWith.Name("store")
                       .Name("book")
                       .Array()
                       .Name("title");

对于您的示例$.store.book[?(@.category == 'fiction')].title,您将使用

var path = JsonPathWith.Name("store")
                       .Name("book")
                       .Array(jv => jv.Name("category") == "fiction")
                       .Name("title");

更重要的是,对这些表达式中的字段的支持也有限。

如果您的路径源是字符串,Manatee.Json 也会处理路径解析!

于 2015-10-05T22:35:44.750 回答