JArray.Remove(JToken)不删除 [英] JArray.Remove(JToken) does not delete

查看:463
本文介绍了JArray.Remove(JToken)不删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有JSON的JObject:

{"name" : "user1", "groups" : ["group 1", "group2"]}

我想按名称删除一个组.所以我有这样的代码:

JObject userJson = JObject.Parse(user);

JArray groups = userJson["groups"] as JArray;
JToken group = new JValue (groupName);

groups.Remove(group);

但是方法JArray.remove(Jtoken item)返回false(这意味着它不能被删除).这里的信息:

https://www.newtonsoft.com/json/help/html /M_Newtonsoft_Json_Linq_JArray_Remove.htm

因此尝试设置像JToken group = new JValue (groupName); JToken group = new JValue (groupName); as JTokenJValue group = new JValue (groupName);这样的参数,但是它不起作用:S

这个人很好地解释了JToken层次结构,但是我不知道我做错了什么.

https://stackoverflow.com/a/38560188/8849646

解决方案

基本问题是 Parent ,并且每个父母都知道其 Children .实际上,如果您将已经有一个父代的令牌添加到父代,则会被克隆,如此处所述. >

因此,由于每个令牌都知道其父级,因此当您尝试从父级中删除令牌时,Json.NET可能会执行以下两项操作之一:

  1. 如果子对象实际上属于父对象,则可能会将其从父对象中移除(使用引用等式),或者
  2. 如果子级具有作为父级某个子级的保存值,则可能会将其从父级中移除.

事实上,Json.NET选择了前一个选项. Jarray.Remove(JToken item) 调用 JContainer.RemoveItem() 调用 JArray.IndexOfItem() 确定要删除的项目的索引.反过来,此方法使用引用相等:

internal override int IndexOfItem(JToken item)
{
    return _values.IndexOfReference(item);
}

由于您的JToken group = new JValue (groupName)不属于JArray groups,因此不会将其删除.

那么,按值删除JSON数组项的选项是什么?您可以:

  • 使用LINQ搜索:

    groups.Where(i => i.Type == JTokenType.String && (string)i == groupName).ToList().ForEach(i => i.Remove());
    

  • 使用 JTokenEqualityComparer 进行搜索用于搜索复杂对象以及原始值:

    var comparer = new JTokenEqualityComparer();
    groups.Where(i => comparer.Equals(i, group)).ToList().ForEach(i => i.Remove());
    

  • 使用 SelectTokens() :

    userJson.SelectTokens(string.Format("groups[?(@ == '{0}')]", groupName)).ToList().ForEach(i => i.Remove());
    

    SelectTokens()支持 JSONPath查询语法,它可以在数组中搜索匹配项.

最后,关于RemoveItem()文档的注释.声明(添加斜体):

JArray.Remove Method (JToken)

从JArray中删除特定对象的 first 出现.

由于我们已经看到具有父级的令牌在添加到父级时会被克隆,因此在给定的父级中似乎永远都不会出现任何令牌.然而,文档似乎暗示着其他情况.我可能会猜测,这个特定的文档句子已经过时,并且可以追溯到Json.NET的早期版本,在同一版本中同一令牌可能多次出现在父级中.

I have a JObject with a JSON like it:

{"name" : "user1", "groups" : ["group 1", "group2"]}

I would like to delete one group by the name. So I have a code like this:

JObject userJson = JObject.Parse(user);

JArray groups = userJson["groups"] as JArray;
JToken group = new JValue (groupName);

groups.Remove(group);

However the method JArray.remove(Jtoken item) return false (that means that it couldn't be deleted). Here the information:

https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Linq_JArray_Remove.htm

So tried to set the parameter like JToken group = new JValue (groupName); JToken group = new JValue (groupName); as JToken and JValue group = new JValue (groupName); But it doesnt work :S

This guy explains JToken hierarchy very good but I don't know what I'm not doing right.

https://stackoverflow.com/a/38560188/8849646

解决方案

The basic issue is that the JToken hierarchy is doubly-connected graph. That is, each token knows its Parent and each parent knows its Children. Indeed, if you add a token that already has a parent to a parent, it gets cloned, as explained here.

Thus, since every token knows its parent, when you try to remove a token from a parent, Json.NET might do one of two things:

  1. It might remove the child from the parent if the child actually belongs to the parent (using reference equality), OR
  2. It might remove the child from the parent if the child has the save values as some child of the parent.

And, in fact, Json.NET chooses the former option. Jarray.Remove(JToken item) calls JContainer.RemoveItem() which calls JArray.IndexOfItem() to determine the index of the item to remove. This method, in turn, uses reference equality:

internal override int IndexOfItem(JToken item)
{
    return _values.IndexOfReference(item);
}

Since your JToken group = new JValue (groupName) does not belong to the JArray groups, it is not removed.

So, what are your options to remove a JSON array item by value? You could:

  • Search using LINQ:

    groups.Where(i => i.Type == JTokenType.String && (string)i == groupName).ToList().ForEach(i => i.Remove());
    

  • Search using JTokenEqualityComparer, which can be used to search for complex objects as well as primitive values:

    var comparer = new JTokenEqualityComparer();
    groups.Where(i => comparer.Equals(i, group)).ToList().ForEach(i => i.Remove());
    

  • Search using SelectTokens():

    userJson.SelectTokens(string.Format("groups[?(@ == '{0}')]", groupName)).ToList().ForEach(i => i.Remove());
    

    SelectTokens() supports the JSONPath query syntax which enables searching though arrays for matching items.

Finally, a note about the documentation for RemoveItem(). It states (italics added):

JArray.Remove Method (JToken)

Removes the first occurrence of a specific object from the JArray.

Since we have seen that a token that has a parent is cloned when added to a parent, it seems there can only ever be one occurrence of any token within a given parent. Yet the documentation seems to imply otherwise; I would hazard a guess that this particular documentation sentence is obsolete and dates from some much earlier version of Json.NET where the same token could appear within a parent multiple times.

这篇关于JArray.Remove(JToken)不删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆