JSON.net 与 XPATH:如何在 SelectTokens 中保留节点顺序?

XPath 2 声明选择的节点顺序应该按照它们在文档中的顺序返回。当您在 JSON.Net 中使用 SelectTokens(JSONPath) 时,情况似乎并非如此


当我处理以下文件时


string json = @"

{

  ""Files"": {

    ""dir1"": {

      ""Files"": {

        ""file1.1.txt"": {

         ""size:100""},

        ""file1.2.txt"": {

         ""size:100""}

      }

    },

    ""dir2"": {

      ""Files"": {

        ""file2.1.txt"": {

         ""size:100""},

        ""file2.2.txt"": {

         ""size:100""}

      }

    },

    ""file3.txt"": {

     ""size:100""}

  }

}";

使用 JSON.net SelectTokens("$..files.*") 时的顺序如下


dir1

dir2

file3.txt

 file1.1.txt

 file1.2.txt

 file2.1.txt

 file2.2.txt

当我期望以下顺序时(如 Xpath //files/*)


dir1

 file1.1.txt

 file1.2.txt

dir2

 file2.1.txt

 file2.2.txt

file3.txt

我应该如何编写查询,以便按 XPath 顺序获取列表?


森栏
浏览 206回答 1
1回答

慕标5832272

除了修改 Json.Net 源代码之外,我无法直接控制SelectTokens()返回结果的顺序。它似乎使用广度优先排序。SelectTokens()您可以将 LINQ-to-JSON 查询与该Descendants()方法一起使用,而不是使用。这将按深度优先顺序返回令牌。但是,您需要过滤掉您不感兴趣的属性名称,例如“文件”和“大小”。string json = @"{&nbsp; ""Files"": {&nbsp; &nbsp; ""dir1"": {&nbsp; &nbsp; &nbsp; ""Files"": {&nbsp; &nbsp; &nbsp; &nbsp; ""file1.1.txt"": { ""size"": 100 },&nbsp; &nbsp; &nbsp; &nbsp; ""file1.2.txt"": { ""size"": 100 }&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; },&nbsp; &nbsp; ""dir2"": {&nbsp; &nbsp; &nbsp; ""Files"": {&nbsp; &nbsp; &nbsp; &nbsp; ""file2.1.txt"": { ""size"": 100 },&nbsp; &nbsp; &nbsp; &nbsp; ""file2.2.txt"": { ""size"": 100 }&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; },&nbsp; &nbsp; ""file3.txt"": { ""size"": 100 }&nbsp; }}";JObject jo = JObject.Parse(json);var files = jo.Descendants()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .OfType<JProperty>()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Select(p => p.Name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Where(n => n != "Files" && n != "size")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .ToArray();Console.WriteLine(string.Join("\n", files));小提琴:https : //dotnetfiddle.net/yRAev4如果您不喜欢这个想法,另一种可能的解决方案是使用自定义IComparer<T>在事后将所选属性排序回其原始文档顺序:class JPropertyDocumentOrderComparer : IComparer<JProperty>{&nbsp; &nbsp; public int Compare(JProperty x, JProperty y)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var xa = GetAncestors(x);&nbsp; &nbsp; &nbsp; &nbsp; var ya = GetAncestors(y);&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < xa.Count && i < ya.Count; i++)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!ReferenceEquals(xa[i], ya[i]))&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return IndexInParent(xa[i]) - IndexInParent(ya[i]);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return xa.Count - ya.Count;&nbsp; &nbsp; }&nbsp; &nbsp; private List<JProperty> GetAncestors(JProperty prop)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return prop.AncestorsAndSelf().OfType<JProperty>().Reverse().ToList();&nbsp; &nbsp; }&nbsp; &nbsp; private int IndexInParent(JProperty prop)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; int i = 0;&nbsp; &nbsp; &nbsp; &nbsp; var parent = (JObject)prop.Parent;&nbsp; &nbsp; &nbsp; &nbsp; foreach (JProperty p in parent.Properties())&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (ReferenceEquals(p, prop)) return i;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i++;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return -1;&nbsp; &nbsp; }}像这样使用比较器:JObject jo = JObject.Parse(json);var files = jo.SelectTokens("$..Files")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .OfType<JObject>()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .SelectMany(j => j.Properties())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .OrderBy(p => p, new JPropertyDocumentOrderComparer())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Select(p => p.Name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .ToArray();Console.WriteLine(string.Join("\n", files));小提琴:https : //dotnetfiddle.net/xhx7Kk
打开App,查看更多内容
随时随地看视频慕课网APP