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:
- It might remove the child from the parent if the child actually belongs to the parent (using reference equality), OR
- It might remove the child from the parent if the child has the same 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.