web-dev-qa-db-ja.com

jsonを逆シリアル化するコンストラクターにパラメーターを渡す方法

Newtonsoft.Jsonを使用してオブジェクトを逆シリアル化するときに、親インスタンスをコンストラクターに渡す際に小さな問題が発生します。

私が次のクラスを持っていると仮定しましょう

public class A
{
    public string Str1 { get; set; }

    public IList<B> Bs { get; set; }
}

public class B
{
    public B(A a)
    {
        // a should not be null!
        Console.WriteLine(a.Str)
    }
}

そして今、私は次のようにオブジェクトaをserailzeして逆シリアル化します。

A a = new A()
a.Bs = new List<B>()
a.Bs.Add(new B(a));
a.Bs.Add(new B(a));
a.Bs.Add(new B(a));

var json = JsonConvert.SerializeObject(a);

// Here i need to call the constructor of B when creating new instances
var newA = JsonConvert.DeserializeObject<A>(json);

問題は、オブジェクトを逆シリアル化するときに、nullBのコンストラクターに渡されることです。誰かが以前にこの問題/問題を解決したことがありますか?

どうもありがとうございました!

17
BendEg

あなたの質問とコメントで、クラスBにはAのパブリックプロパティがないと述べました。したがって、Bをシリアル化すると、Json.Netはデフォルトで公開情報のみをシリアル化するため、AはJSONに書き込まれません。したがって、逆シリアル化する場合、JSONにBがないため、Aを再作成するのに十分な情報がありません。したがって、ステップ1は、BAへの参照をJson.Netに表示することです。公開したくない場合は問題ありませんが、少なくともメンバーを[JsonProperty]属性でマークして、Json.Netがそれを「参照」できるようにする必要があります。

public class B
{
    [JsonProperty]
    private A a;

    public B(A a)
    {
        this.a = a;  // be sure to set the A member in your constructor
    }
}

上記を実行すると、2番目の問題が発生します。クラス構造に参照ループがあります(AにはそれぞれがBを参照するAsのリストがあります) 、この場合、シリアライザーはデフォルトで例外をスローします。解決策は、シリアライザーの PreserveReferencesHandling 設定をObjectsに設定することです(デフォルトはNoneです)。これにより、シリアライザがシリアル化中に参照ループを処理できるようになるだけでなく、逆シリアル化中に元の参照が保持されるため、すべてのBsが同じAインスタンスを参照するようになります。 (これは、JSONに書き込まれる特別な$idおよび$refプロパティを介して実行されます。)

JsonSerializerSettings settings = new JsonSerializerSettings
{
    PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};

var json = JsonConvert.SerializeObject(a, settings);

var newA = JsonConvert.DeserializeObject<A>(json, settings);

実例: https://dotnetfiddle.net/N0FUID

12
Brian Rogers

コンストラクターでオブジェクトを渡す必要があるのは、最初にデフォルトコンストラクターを使用してオブジェクトを作成し、次にpopulateオブジェクトを呼び出して、プロパティを[JsonIgore]で装飾したときにスキップされないすべてのプロパティを設定することです。

var settings = new JsonSerializerSettings() 
{ 
  Error = HandleJsonDeserializationError,
  PreserveReferencesHandling = PreserveReferencesHandling.Objects 
}
var myObject = new ComplexObject(param1,param2);
JsonConvert.PopulateObject(json, myObject, settings);

JsonSettingsプロパティでシリアル化エラーを処理すれば、オブジェクトの入力を続行して問題に対処できます。署名は次のとおりです。

static void HandleJsonDeserializationError(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs errorArgs)
{
   var currentError = errorArgs.ErrorContext.Error.Message;
   errorArgs.ErrorContext.Handled = true;
   //loging framework logs the error, set brake point etc when debug.
   Logger.Log(currentError, LogLevel.Exceptions);
}
0
Walter Vehoeven