为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
森栏 2021-11-21 18:11:30
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.*") 时的顺序如下dir1dir2file3.txt file1.1.txt file1.2.txt file2.1.txt file2.2.txt当我期望以下顺序时(如 Xpath //files/*)dir1 file1.1.txt file1.2.txtdir2 file2.1.txt file2.2.txtfile3.txt我应该如何编写查询,以便按 XPath 顺序获取列表?
查看完整描述

1 回答

?
慕标5832272

TA贡献1966条经验 获得超4个赞

除了修改 Json.Net 源代码之外,我无法直接控制SelectTokens()返回结果的顺序。它似乎使用广度优先排序。


SelectTokens()您可以将 LINQ-to-JSON 查询与该Descendants()方法一起使用,而不是使用。这将按深度优先顺序返回令牌。但是,您需要过滤掉您不感兴趣的属性名称,例如“文件”和“大小”。


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 }

  }

}";


JObject jo = JObject.Parse(json);


var files = jo.Descendants()

              .OfType<JProperty>()

              .Select(p => p.Name)

              .Where(n => n != "Files" && n != "size")

              .ToArray();


Console.WriteLine(string.Join("\n", files));

小提琴:https : //dotnetfiddle.net/yRAev4


如果您不喜欢这个想法,另一种可能的解决方案是使用自定义IComparer<T>在事后将所选属性排序回其原始文档顺序:


class JPropertyDocumentOrderComparer : IComparer<JProperty>

{

    public int Compare(JProperty x, JProperty y)

    {

        var xa = GetAncestors(x);

        var ya = GetAncestors(y);

        for (int i = 0; i < xa.Count && i < ya.Count; i++)

        {

            if (!ReferenceEquals(xa[i], ya[i])) 

            {

                return IndexInParent(xa[i]) - IndexInParent(ya[i]);

            }

        }

        return xa.Count - ya.Count;

    }


    private List<JProperty> GetAncestors(JProperty prop)

    {

        return prop.AncestorsAndSelf().OfType<JProperty>().Reverse().ToList();

    }


    private int IndexInParent(JProperty prop)

    {

        int i = 0;

        var parent = (JObject)prop.Parent;

        foreach (JProperty p in parent.Properties())

        {

            if (ReferenceEquals(p, prop)) return i; 

            i++;

        }

        return -1;

    }

}

像这样使用比较器:


JObject jo = JObject.Parse(json);


var files = jo.SelectTokens("$..Files")

              .OfType<JObject>()

              .SelectMany(j => j.Properties())

              .OrderBy(p => p, new JPropertyDocumentOrderComparer())

              .Select(p => p.Name)

              .ToArray();


Console.WriteLine(string.Join("\n", files));

小提琴:https : //dotnetfiddle.net/xhx7Kk


查看完整回答
反对 回复 2021-11-21
  • 1 回答
  • 0 关注
  • 205 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信