Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
854 views
in Technique[技术] by (71.8m points)

c# - Getting the error "Cannot add or remove items from Newtonsoft.Json.Linq.JProperty" in Json.net

So I'm trying to control deserialization by reading a json object as a JObject, deleting some fields, and then deserializing it again to my target object using Json.Net. The problem is, any time I try to delete a field, I get the error:

An unhandled exception of type 'Newtonsoft.Json.JsonException' occurred in Newtonsoft.Json.dll

Additional information: Cannot add or remove items from Newtonsoft.Json.Linq.JProperty.

Here's my (simplified, but still causing the error) code:

JToken token = (JToken)JsonConvert.DeserializeObject(File.ReadAllText(fileName));

foreach (JToken inner in token["docs"])
{
    if (inner["_id"] != null)
        inner["_id"].Remove();

    MyObject read = new MyObject();
    JsonConvert.PopulateObject(inner.ToString(), read);
    Values.Add((MyObject)JsonConvert.DeserializeObject(inner.ToString(), typeof(MyObject)));
}

The json is a very large file where the docs array contains many elements as follows (again simplified for clarity):

{
    "docs": [
        {
            "Time": "None",
            "Level": 1,
            "_id": "10208"              
        },
        {
            "Time": "None",
            "Level": 1,
            "_id": "10209"
        }
    ]
}

Alternatively if there's a better way to deserialize JSON to a specific type, but still ignoring additional fields, that would be a fine alternative.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Assuming Values is a List<MyObject> and your MyObject class looks like this:

class MyObject
{
    public string Time { get; set; }
    public int Level { get; set; }
}

you can replace all that code with the following to get the result you want:

string json = File.ReadAllText(fileName);
Values = JToken.Parse(json)["docs"].ToObject<List<MyObject>>();

This works because Json.Net will ignore missing properties by default. Since the MyObject class does not contain an _id property to deserialize into, you don't need to jump through hoops trying to remove it from the JSON.

Explanation of why Remove() didn't work

JToken.Remove() removes a JToken from its parent. It is legal to remove a JProperty from its parent JObject, or to remove a child JToken from a JArray. However, you cannot remove the value from a JProperty. A JProperty must always have exactly one value.

When you ask for token["_id"] you get back the value of the JProperty called _id, not the JProperty itself. Therefore you will get an error if you try to call Remove() on that value. To make it work the way you are doing, you'd need to use Parent like this:

if (inner["_id"] != null)
    inner["_id"].Parent.Remove();

This says "Find the property whose name is _id and give me the value. If it exists, get that value's parent (the property), and remove it from its parent (the containing JObject)."

A more straightforward way to do it is to use the Property() method to access the property directly. However, this method is only available on JObject, not JToken, so you would either need to change the declaration of inner to a JObject or cast it:

foreach (JObject inner in token["docs"].Children<JObject>())
{
    JProperty idProp = inner.Property("_id");
    if (idProp != null)
        idProp.Remove();
    ...
}

Lastly, as mentioned in the comments, if you're using C# 6 or later you can shorten the code a bit using the null-conditional operator:

    inner.Property("_id")?.Remove();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...