如何通过值查找json令牌并删除该令牌。

3
例如,我在C#中有一个像这样的json:
{
"Harry.firstName": "Harry",
"Harry.lastName": "Birimirski",
"Harry.recordTitle": "My title",
"Harry.SomeRepeatable": [{
        "GUID": "9dd8c7bb-64e1-452b-90d5-db2b6e505492",
        "NestedRepetable": [{
                "GUID": "05aa2161-fcc2-45a6-b0b7-8749d94a2e61",
                "nestedText": "Nested first"
            },
            {
                "GUID": "dfd67eeb-703b-4cc4-9321-b6a39084e687",
                "nestedText": "Nested first 2"
            }
        ],
        "name": "First"
    },
    {
        "GUID": "3318e544-1be8-4795-9bab-fa05de79cf46",
        "NestedRepetable": [{
            "GUID": "c1b60869-7c75-4af4-8037-be26aeca7939",
            "nestedText": "Nested second"
        }],
        "name": "Second"
    }
]
}

我想通过值从 NestedRepetable 数组中删除项目。例如,删除包含 GUID:05aa2161-fcc2-45a6-b0b7-8749d94a2e61 的元素,期望的结果将如下所示:
{
"Harry.firstName": "Harry",
"Harry.lastName": "Birimirski",
"Harry.recordTitle": "My title",
"Harry.SomeRepeatable": [{
        "GUID": "9dd8c7bb-64e1-452b-90d5-db2b6e505492",
        "NestedRepetable": [
            {
                "GUID": "dfd67eeb-703b-4cc4-9321-b6a39084e687",
                "nestedText": "Nested first 2"
            }
        ],
        "name": "First"
    },
    {
        "GUID": "3318e544-1be8-4795-9bab-fa05de79cf46",
        "NestedRepetable": [{
            "GUID": "c1b60869-7c75-4af4-8037-be26aeca7939",
            "nestedText": "Nested second"
        }],
        "name": "Second"
    }
]
}

我尝试了一些Json.NET的方法,但是无法做到。 请注意,整个JSON结构是动态的。我唯一知道的是这种情况下的字段名称(GUID)和字段中应该被删除的值。
我尝试按照question中描述的方式进行操作, 但他们使用的是json的硬编码路径。这不是我的情况。
我使用了以下代码:
        private JToken RemoveFields(JToken token, string fieldValue)
    {
        JContainer container = token as JContainer;
        if (container == null)
        {
            return token;
        }

        List<JToken> removeList = new List<JToken>();
        foreach (JToken el in container.Children())
        {
            JProperty p = el as JProperty;
            if (p != null)// && fields.Contains(p.Name))
            {
                if (p.Value.ToString() == fieldValue)
                {
                    removeList.Add(el);

                    //try to remove the whole thing, not only GUID field ..
                    //removeList.Add(el.Parent);

                }
            }

            RemoveFields(el, fieldValue);
        }

        foreach (JToken el in removeList)
        {
            el.Parent.Remove();
            return token;
        }

        return token;
    }

但是我收到了以下错误信息:
“集合已被修改;枚举操作可能无法执行”
可能是因为我试图删除父元素。

如果只有一个命中,您可以在删除对象后返回。由于您正在使用迭代器(foreach会这样做),因此在迭代时无法添加或删除项目,这会导致问题。那么,在删除后直接返回是否可行? - Sami Kuhmonen
你指向的示例是在迭代完成后进行删除的。由于你正在递归调用函数,最有可能发生的情况是它完成内部迭代并尝试删除一些元素,而外部迭代仍在进行中。尝试构建要删除的项目列表,然后确保在任何JSON遍历之外将它们删除。 - Andrei U
你尝试将 ToList() 添加到你的 removeList 迭代中了吗?这样就足够了吗? - ColinM
ToList() 对我不起作用,它返回整个令牌,而没有从数组中删除的元素。我同意你们所有人的观点 - 问题在于集合被修改了,但我找不到一种方法来返回正确的结果。 - Harry Birimirski
类似于这个答案,看起来接近你想要的,即删除具有特定值的“GUID”属性的对象。 - dbc
1个回答

1

您正在尝试查找并删除符合以下条件的对象:

  1. 所有包含在名为"NestedRepetable"的数组属性中的对象
  2. 具有特定值的名为"GUID"的属性。

最简单的方法是使用JToken.SelectTokens()JSONPath查询

var value = "05aa2161-fcc2-45a6-b0b7-8749d94a2e61";

var queryStringTemplate = "..NestedRepetable[?(@.GUID == '{0}')]";
var query = root.SelectTokens(string.Format(queryStringTemplate, value));

foreach (var obj in query.ToList())
    obj.Remove();

如果您并不在乎对象是否嵌套在“NestedRepetable”内(您的问题不太清楚),您可以执行以下操作
var queryStringTemplate = "..[?(@.GUID == '{0}')]";

注意:

  • .. 是递归下降运算符。它沿着 JToken 层次结构向下返回所有值。

  • NestedRepetable 匹配具有所需名称的属性的值。

  • [?(@.GUID == '{0}')] 匹配属于具有指定值的名为 GUID 的属性的 NestedRepetable 数组的对象。

  • 在移除与查询匹配的令牌时,需要通过调用 ToList() 来实现查询的物化,以避免您看到的 Collection was modified 异常。

  • 有关 JSONPath 语法的更多信息,请参见 JSONPath - XPath for JSON

复杂查询的 .Net 工作示例 此处,简单查询的示例 此处


这个做法很好,非常感谢!虽然是不同的方法,但解决方案更清晰。看来我需要花些时间来更好地理解JSONPath。 - Harry Birimirski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接