web-dev-qa-db-ja.com

Json.netで「Newtonsoft.Json.Linq.JPropertyにアイテムを追加または削除できません」というエラーが発生する

したがって、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を特定の型に逆シリアル化するより良い方法があり、それでも追加のフィールドを無視する場合、それは良い代替案です。

32
Migwell

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から値を削除することはできません。 JPropertyには常に正確に1つの値が必要です。

_token["_id"]_を要求すると、JProperty自体ではなく、__id_と呼ばれるJPropertyvalueが返されます。したがって、その値に対してRemove()を呼び出そうとすると、エラーが発生します。それをあなたがやっている方法で機能させるには、次のようにParentを使用する必要があります:

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

これは、「名前が__id_のプロパティを見つけて、値を指定します。存在する場合は、その値の親(プロパティ)を取得し、その親(それを含むJObject)から削除します。」

より簡単な方法は、Property()メソッドを使用してプロパティに直接アクセスすることです。ただし、このメソッドはJObjectではなくJTokenでのみ使用できるため、innerの宣言をJObjectに変更するか、キャストする必要があります。

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

最後に、コメントで述べたように、C#6以降を使用している場合は、null条件演算子を使用してコードを少し短くできます。

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