web-dev-qa-db-fra.com

Obtention de l'erreur "Impossible d'ajouter ou de supprimer des éléments de Newtonsoft.Json.Linq.JProperty" dans Json.net

J'essaie donc de contrôler la désérialisation en lisant un objet json en tant que JObject, en supprimant certains champs, puis en le désérialisant à nouveau vers mon objet cible à l'aide de Json.Net . Le problème est, chaque fois que j'essaie de supprimer un champ, j'obtiens l'erreur:

Une exception non gérée de type "Newtonsoft.Json.JsonException" s'est produite dans Newtonsoft.Json.dll

Informations supplémentaires: impossible d'ajouter ou de supprimer des éléments de Newtonsoft.Json.Linq.JProperty.

Voici mon code (simplifié, mais toujours à l'origine de l'erreur):

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)));
}

Le json est un très gros fichier où le tableau des documents contient de nombreux éléments comme suit (encore une fois simplifié pour plus de clarté):

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

Sinon, s'il existe un meilleur moyen de désérialiser JSON en un type spécifique, mais en ignorant toujours les champs supplémentaires, ce serait une bonne alternative.

32
Migwell

En supposant que Values est un List<MyObject> Et que votre classe MyObject ressemble à ceci:

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

vous pouvez remplacer tout ce code par ce qui suit pour obtenir le résultat souhaité:

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

Cela fonctionne car Json.Net ignorera les propriétés manquantes par défaut. Étant donné que la classe MyObject ne contient pas de propriété _id Dans laquelle désérialiser, vous n'avez pas besoin de parcourir les cadres pour essayer de la supprimer du JSON.

Explication de la raison pour laquelle Remove() n'a pas fonctionné

JToken.Remove() supprime un JToken de son parent. Il est légal de supprimer un JProperty de son parent JObject, ou de supprimer un enfant JToken d'un JArray. Cependant, vous ne pouvez pas supprimer la valeur d'un JProperty. Un JProperty doit toujours avoir exactement une valeur.

Lorsque vous demandez token["_id"], Vous récupérez la valeur du JProperty appelé _id, Pas le JProperty lui-même. Par conséquent, vous obtiendrez une erreur si vous essayez d'appeler Remove() sur cette valeur. Pour que cela fonctionne comme vous le faites, vous devez utiliser Parent comme ceci:

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

Cela dit "Trouvez la propriété dont le nom est _id Et donnez-moi la valeur. Si elle existe, récupérez le parent de cette valeur (la propriété) et supprimez-la de son parent (le JObject contenant) . "

Une manière plus simple de le faire consiste à utiliser la méthode Property() pour accéder directement à la propriété. Cependant, cette méthode n'est disponible que sur JObject, pas JToken, vous devrez donc soit changer la déclaration de inner en JObject, soit la caster :

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

Enfin, comme mentionné dans les commentaires, si vous utilisez C # 6 ou une version ultérieure, vous pouvez raccourcir un peu le code à l'aide de l'opérateur null-conditionnel:

    inner.Property("_id")?.Remove();
68
Brian Rogers