Получение ошибки "Невозможно добавить или удалить элементы из Newtonsoft.Json.Linq.JProperty" в Json.net

Итак, я пытаюсь контролировать десериализацию, читая json-объект как JObject, удаляя некоторые поля, а затем десериализуя его снова на свой целевой объект, используя Json.Net. Проблема в том, что всякий раз, когда я пытаюсь удалить поле, я получаю сообщение об ошибке:

Необработанное исключение типа Newtonsoft.Json.JsonException ' произошел в Newtonsoft.Json.dll

Дополнительная информация: нельзя добавлять или удалять элементы из 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)));
}

json - очень большой файл, в котором массив docs содержит много элементов следующим образом (снова упрощен для ясности):

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

Альтернативно, если есть лучший способ десериализации JSON для определенного типа, но все же игнорирование дополнительных полей, это была бы прекрасная альтернатива.

Ответы

Ответ 1

Предполагая, что Values является List<MyObject>, а ваш класс MyObject выглядит следующим образом:

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

вы можете заменить весь этот код следующим, чтобы получить желаемый результат:

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

Это работает, потому что Json.Net по умолчанию игнорирует отсутствующие свойства. Поскольку класс MyObject не содержит свойство _id для десериализации, вам не нужно прыгать через обручи, пытаясь удалить его из JSON.

Объяснение причины Remove() не работает

JToken.Remove() удаляет JToken из своего родителя. Допустимо удалить JProperty из родительского JObject или удалить дочерний элемент JToken из JArray. Однако вы не можете удалить значение из JProperty. A JProperty должно всегда иметь ровно одно значение.

Когда вы запрашиваете token["_id"], вы возвращаете значение JProperty, называемое _id, а не JProperty. Поэтому вы получите сообщение об ошибке, если попытаетесь вызвать Remove() на это значение. Чтобы он работал так, как вы делаете, вам нужно будет сделать это:

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

Это говорит: "Найдите свойство, имя которого _id и дайте мне значение. Если оно существует, получите это значение parent (свойство) и удалите его из его родителя (содержащего JObject)."

Другой способ сделать это, возможно, немного более ясен, это сказать:

JProperty id = inner.Children<JProperty>().FirstOrDefault(p => p.Name == "_id");
if (id != null)
    id.Remove();

Ответ 2

Основываясь на бриллианском ответе Брайана, вы можете сделать это просто в своем случае:

var inner_id = inner["_id"] as JProperty;
if (inner_id != null)
  inner_id.Remove();