23

Newtonsoft.Json(Json.net)を使い始めたところです。最初の簡単なテストで、汎用リストを逆シリアル化するときに問題が発生しました。以下のコードサンプルでは、​​3種類の単純な整数リスト(プロパティ、メンバー変数、配列)を含むオブジェクトをシリアル化します。

結果のjsonは正常に見えます(リストはjson-arraysに変換されます)。ただし、jsonを逆シリアル化して同じタイプの新しいオブジェクトに戻すと、すべてのリストアイテムが複製され、配列が必要になります。もう一度シリアル化することでそれを説明しました。

周りを検索して、デシリアライザーも入力するリストに「プライベート」バッキングフィールドがある可能性があることを読みました。

だから私の質問は:次の場合に重複アイテムを避けるための(できれば簡単な)方法はありますか?

コード

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace JsonSerializeExample
{
    public class Program
    {
        static void Main()
        {
            var data = new SomeData();
            var json = JsonConvert.SerializeObject(data);
            Console.WriteLine("First : {0}", json);
            var data2 = JsonConvert.DeserializeObject<SomeData>(json);
            var json2 = JsonConvert.SerializeObject(data2);
            Console.WriteLine("Second: {0}", json2);
        }
    }

    public class SomeData
    {
        public string SimpleField;
        public int[] IntArray;
        public IList<int> IntListProperty { get; set; }
        public IList<int> IntListMember;

        public SomeData()
        {
            SimpleField = "Some data";
            IntArray = new[] { 7, 8, 9 };
            IntListProperty = new List<int> { 1, 2, 3 };
            IntListMember = new List<int> { 4, 5, 6 };
        }
    }
}

結果の出力

First : {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6],"IntListProperty":[1,2,3]}
Second: {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6,4,5,6],"IntListProperty":[1,2,3,1,2,3]}

ここでは、Json.Netの重複するプライベートリストアイテムと重複している可能性があります。しかし、私の問題はもっと単純だと思います、そして私はまだそれを理解していません。

4

4 に答える 4

27

これは、コンストラクターにアイテムを追加しているためです。リストを処理するときのデシリアライザーの一般的なアプローチは、基本的に次のとおりです。

  • ゲッターでリストを読む
    • リストがnullの場合:新しいリストを作成し、プロパティセッターを介して割り当てます(ある場合)
  • 各アイテムを順番に逆シリアル化Addし、リストに()を追加します

これは、ほとんどのリストメンバーがセッターを持っていないためです。

public List<Foo> Items {get {...}} // <=== no set

有用であるためにはセッターが必要な配列とは対照的です。したがって、アプローチは通常次のとおりです。

  • 各アイテムを順番に逆シリアル化Addし、一時リストに()を追加します
  • リストを配列(ToArray)に変換し、セッターを介して割り当てます

一部のシリアライザーは、この動作を制御するためのオプションを提供します(他のシリアライザーは制御しません)。また、一部のシリアライザーは、コンストラクターを完全にバイパスする機能を提供します(他のシリアライザーはバイパスしません)。

于 2012-11-15T09:18:32.980 に答える
12

この投稿はもう関係がないと確信していますが、今後の参考のために、ここで実用的な解決策を紹介します。ObjectCreationHandlingに設定されていることを指定する必要がありますReplace。つまり、(デフォルトでは)ではなく常に新しいオブジェクトを作成しますAuto。つまり、既存のオブジェクトを再利用し、必要に応じて新しいオブジェクトを作成します。

var data = new SomeData(); 
var json = JsonConvert.SerializeObject(data);
Console.WriteLine("First : {0}", json);
var data2 = JsonConvert.DeserializeObject<SomeData>(json, new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace });
var json2 = JsonConvert.SerializeObject(data2);
Console.WriteLine("Second: {0}", json2);
于 2019-11-13T21:47:26.137 に答える
10

根本的な原因が異なる同様の問題が発生しました。私は次のようなクラスをシリアル化および逆シリアル化していました。

public class Appointment
{
    public List<AppointmentRevision> Revisions { get; set; }

    public AppointmentRevision CurrentRevision
    {
        get { return Revision.LastOrDefault(); }
    }

    public Appointment()
    {
        Revisions = new List<AppointmentRevision>();
    }
}

public class AppointmentRevision
{
    public List<Attendee> Attendees { get; set; }
}

これをシリアル化したとき、CurrentRevisionもシリアル化されていました。方法はわかりませんが、逆シリアル化すると、AppointmentRevisionの単一のインスタンスが正しく保持されていましたが、参加者リストに重複が作成されていました。解決策は、CurrentRevisionプロパティのJsonIgnore属性を使用することでした。

public class Appointment
{
    public List<AppointmentRevision> Revisions { get; set; }

    [JsonIgnore]   
    public AppointmentRevision CurrentRevision
    {
        get { return Revision.LastOrDefault(); }
    }

    public Appointment()
    {
        Revisions = new List<AppointmentRevision>();
    }
}
于 2014-10-08T17:35:58.027 に答える
1

JSONを逆シリアル化するときに、選択したプロパティにObjectCreationHandling.Replaceを適用するにはどうすればよいですか?

結局のところ(私は2019年です)、質問で行っていたように、コンストラクターでリスト項目を設定できます。リストの宣言の上にObjectCreationHandling.Replace属性を追加しました。その後、シリアル化により、リストに格納されているものがすべてJSONに置き換えられます。

于 2019-04-24T17:14:24.750 に答える