Newtonsoft.json:根据json路径白名单剪切JSON

2024-03-26

假设我有一些复杂的 JSON:

{
    "path1": {
        "path1Inner1": {
            "id": "id1"
        },
        "path1Inner2": {
            "id": "id2"
        }
    },
    "path2": {
        "path2Inner1": {
            "id": "id3"
        },
        "path2Inner2": {
            "id": "id4",
            "key": "key4"
        }
    }
}

还有一些json路径表达式的白名单,例如:

  • $.path1.path1Inner1
  • $.path2.path2Inner2.key

我想在 JSON 树中仅保留与“白名单”匹配的节点和属性,因此,结果将是:

{
    "path1": {
        "path1Inner1": {
            "id": "id1"
        }
    },
    "path2": {
        "path2Inner2": {
            "key": "key4"
        }
    }
}

IE。这不仅仅是通过 JSON 路径进行选择(这是一项简单的任务),而且节点和属性必须保留源 JSON 树中的初始位置。


首先,我非常感谢this https://stackoverflow.com/a/70080357/876743 and this https://stackoverflow.com/a/30333562/876743答案。它们成为我分析问题的起点。

这些答案提出了两种不同的方法来实现按路径“白名单”的目标。这first https://stackoverflow.com/a/70080357/876743一种从头开始重建白名单路径结构(即从空对象开始创建所需的路由)。该实现解析字符串路径并尝试根据解析的路径重建树。这种方法需要非常方便地考虑所有可能的路径类型,因此可能容易出错。你可以在我的评论中找到我发现的一些错误answer https://stackoverflow.com/a/70080357/876743.

The second https://stackoverflow.com/a/30333562/876743方法基于 json.net 对象树 API(Parent、Ancestors、Descendants 等)。该算法遍历树并删除未“列入白名单”的路径。我发现这种方法更容易,更不容易出错,并且“一次性”支持广泛的案例。

我实现的算法在很多方面与second https://stackoverflow.com/a/30333562/876743答案,但我认为,实施和理解要容易得多。另外,我不认为它的性能更差。

    public static class JsonExtensions
    {
        public static TJToken RemoveAllExcept<TJToken>(this TJToken token, IEnumerable<string> paths) where TJToken : JContainer
        {
            HashSet<JToken> nodesToRemove = new(ReferenceEqualityComparer.Instance);
            HashSet<JToken> nodesToKeep = new(ReferenceEqualityComparer.Instance);

            foreach (var whitelistedToken in paths.SelectMany(token.SelectTokens))
                TraverseTokenPath(whitelistedToken, nodesToRemove, nodesToKeep);

            //In that case neither path from paths has returned any token
            if (nodesToKeep.Count == 0)
            {
                token.RemoveAll();
                return token;
            }

            nodesToRemove.ExceptWith(nodesToKeep);

            foreach (var notWhitelistedNode in nodesToRemove)
                notWhitelistedNode.Remove();

            return token;
        }

        private static void TraverseTokenPath(JToken value, ISet<JToken> nodesToRemove, ISet<JToken> nodesToKeep)
        {
            JToken? immediateValue = value;

            do
            {
                nodesToKeep.Add(immediateValue);

                if (immediateValue.Parent is JObject or JArray)
                {
                    foreach (var child in immediateValue.Parent.Children())
                        if (!ReferenceEqualityComparer.Instance.Equals(child, value))
                            nodesToRemove.Add(child);
                }

                immediateValue = immediateValue.Parent;
            } while (immediateValue != null);
        }
    }

要比较 JToken 实例,必须使用引用相等比较器,因为某些 JToken 类型像 JValue 一样使用“按值”比较。否则,在某些情况下您可能会出现错误行为。

例如,有源 JSON

{
   "path2":{
      "path2Inner2":[
         "id",
         "id"
      ]
   }
}

和一条路$..path2Inner2[0]你会得到结果 JSON

{
   "path2":{
      "path2Inner2":[
         "id",
         "id"
      ]
   }
}

代替

{
   "path2":{
      "path2Inner2":[
         "id"
      ]
   }
}

就 .net 5.0 而言,标准ReferenceEqualityComparer可以使用。如果您使用早期版本的 .net,您可能需要实施它 https://stackoverflow.com/a/35520207/876743.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Newtonsoft.json:根据json路径白名单剪切JSON 的相关文章

随机推荐