在Json.net中获取错误"无法在Newtonsoft.Json.Linq.JProperty中添加或删除项"

Mig*_*ell 29 c# serialization json json.net

所以我试图通过将json对象作为JObject读取,删除一些字段,然后使用Json.Net将其再次反序列化为我的目标对象来控制反序列.问题是,每次我尝试删除字段时,都会收到错误:

Newtonsoft.Json.dll中出现未处理的"Newtonsoft.Json.JsonException"类型异常

其他信息:无法添加或删除Newtonsoft.Json.Linq.JProperty中的项目.

这是我的(简化但仍然导致错误)代码:

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)));
}
Run Code Online (Sandbox Code Playgroud)

json是一个非常大的文件,其中docs数组包含许多元素,如下所示(为简洁起见,再次简化):

{
    "docs": [
        {
            "Time": "None",
            "Level": 1,
            "_id": "10208"              
        },
        {
            "Time": "None",
            "Level": 1,
            "_id": "10209"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

或者,如果有更好的方法将JSON反序列化为特定类型,但仍然忽略其他字段,那将是一个很好的选择.

Bri*_*ers 73

假设Values是a List<MyObject>,你的MyObject班级看起来像这样:

class MyObject
{
    public string Time { get; set; }
    public int Level { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下代码替换所有代码以获得所需的结果:

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

这是有效的,因为Json.Net默认会忽略缺少的属性.由于MyObject该类不包含_id要反序列化的属性,因此您无需跳过尝试将其从JSON中删除的环.

解释为什么Remove()不起作用

JToken.Remove()JToken从父项中删除a .将a JProperty从其父项JObject中删除或从中删除子项是合法JTokenJArray.但是,您无法从中删除值JProperty.A JProperty必须始终只有一个值.

当你要求token["_id"]你取回被叫的价值时,而不是自己.因此,如果您尝试调用该值,则会出现错误.为了使它按照你的方式工作,你需要像这样使用:JProperty_idJPropertyRemove()Parent

if (inner["_id"] != null)
    inner["_id"].Parent.Remove();
Run Code Online (Sandbox Code Playgroud)

这表示"查找名称为的属性并为其_id提供值.如果存在,则获取该值的父级(属性),并将其从其父级(包含JObject)中删除."

更直接的方法是使用该Property()方法直接访问属性.但是,此方法仅适用于JObject,JToken因此您可能需要将声明更改innerJObject或转换它:

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

最后,如评论中所述,如果您使用的是C#6或更高版本,则可以使用空条件运算符稍微缩短代码:

    inner.Property("_id")?.Remove();
Run Code Online (Sandbox Code Playgroud)

  • 我已经更新了我的答案,添加了您要求的解释. (3认同)
  • 较短的版本:inner.SelectToken("_id")?.Parent.Remove(); (3认同)