2014-02-20 5 views
15

Итак, я пытаюсь контролировать десериализацию, читая json-объект как объект JObject, удаляя некоторые поля, а затем десериализуя его снова к моему целевому объекту, используя Json.Net. Проблема заключается в том, в любое время я пытаюсь удалить поле, я получаю ошибку:Получение ошибки «Невозможно добавить или удалить элементы из Newtonsoft.Json.Linq.JProperty» в Json.net

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.

Вот мой (упрощенно, но по-прежнему вызывает ошибку) код:

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 к определенному типу, но по-прежнему игнорируя дополнительные поля, тх t будет прекрасной альтернативой.

+0

Пожалуйста, напишите свой JSON –

+0

. Я не уверен, что это актуально, потому что мы знаем, что внутренний ["id"] не является нулевым, но я все равно поставил его на вопрос. – Miguel

ответ

42

Предполагая 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 и дать мне значение, если он существует, получить родитель, что ценность (в. свойство) и удалить его из своего родителя (содержащего JObject). "

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

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

Спасибо, очень полезно знать, что Json.net игнорирует дополнительные свойства. Тем не менее, мне все равно хотелось бы знать, почему я не могу удалить() поля. – Miguel

+3

Я обновил свой ответ, чтобы добавить объяснение, которое вы просили. –

+1

Большое вам спасибо! Это именно то объяснение, которое я искал! – Miguel

-2

основе Brillian ответа от Брайана, вы можете просто делать это в вашем случае:

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

Извините, это не сработает. Как я сказал в своем ответе, использование синтаксиса индексатора даст вам значение 'JProperty', а не' JProperty'. Значение никогда не будет 'JProperty', потому что' JProperties' не может напрямую содержать другие 'JProperties'. Поэтому, когда вы пытаетесь использовать выражение 'inner [" _ id "]' 'JProperty', как вы здесь делаете, оно всегда будет« null », и поэтому' Remove() 'никогда не будет вызываться. Посмотрите сами: https://dotnetfiddle.net/EkTU4o –

+0

А ... Это сбивает с толку, но у меня есть это сейчас. Внезапно код в моем проекте работает по назначению! Несмотря на то, что мой ответ неверен, я позволил ему показать пример того, как не делать этого ... – awe

Смежные вопросы