39

別のオブジェクトへの循環参照を持つオブジェクトがあります。これらのオブジェクト間の関係を考えると、これは適切な設計です。

説明する

Machine => Customer => Machine

予想どおり、Json を使用してマシンまたは顧客オブジェクトをシリアル化しようとすると、問題が発生します。Machine オブジェクトと Customer オブジェクトの間の関係を壊したくないので、この問題を解決する方法がわかりません。この問題を解決するためのオプションは何ですか?

編集

現在、コントローラーの基本クラスが提供する Json メソッドを使用しています。したがって、私が行っているシリアライゼーションは次のような基本的なものです。

Json(machineForm);
4

9 に答える 9

58

アップデート:

は明らかにそれを無視するのでNonSerializedAttribute、使用しないでください。JavaScriptSerializer

代わりに、 in を使用しScriptIgnoreAttributeSystem.Web.Script.Serializationください。

public class Machine
{
    public string Customer { get; set; }

    // Other members
    // ...
}

public class Customer
{
    [ScriptIgnore]
    public Machine Machine { get; set; }    // Parent reference?

    // Other members
    // ...
}

このように、 aMachineをメソッドに投げ込むと、 からへJsonの関係をトラバースしますが、 からに戻ろうとしません。MachineCustomerCustomerMachine

あなたのコードが好きなように行うための関係はまだそこにありますが、JavaScriptSerializer(メソッドによって使用されるJson) はそれを無視します。

于 2010-01-04T23:55:15.093 に答える
33

「json.encodecircularreference」のGoogleからの(現在)3番目の結果であり、上記の回答に(完全に)同意しませんが、ScriptIgnoreAttributeを使用すると、コードのどこにも、JSONの関係を反対方向にトラバースしたくないでしょう。ユースケースが1つあるため、モデルをロックダウンするとは思わない。

この単純なソリューションを使用するように私を刺激しました。

MVCのビューで作業しているので、モデルがあり、コントローラー内のViewData.Modelにモデルを割り当てるだけで、ビュー内のLINQクエリを使用してデータをフラット化し、問題を適切に削除します。このように必要な特定のJSONの循環参照:

var jsonMachines = from m in machineForm
                   select new { m.X, m.Y, // other Machine properties you desire
                                Customer = new { m.Customer.Id, m.Customer.Name, // other Customer properties you desire
                              }};
return Json(jsonMachines);

または、マシン->顧客関係が1 .. *-> *の場合は、次を試してください。

var jsonMachines = from m in machineForm
                   select new { m.X, m.Y, // other machine properties you desire
                                Customers = new List<Customer>(
                                               (from c in m.Customers
                                                select new Customer()
                                                {
                                                   Id = c.Id,
                                                   Name = c.Name,
                                                   // Other Customer properties you desire
                                                }).Cast<Customer>())
                               };
return Json(jsonMachines);
于 2011-04-13T20:14:42.840 に答える
4

同じ問題がある場合に使用します。L2E オブジェクトを IDictionary に「フラット化」する単純な拡張メソッドを作成しました。IDictionary は JavaScriptSerializer によって正しくシリアライズされます。結果の Json は、オブジェクトを直接シリアル化するのと同じです。

シリアル化のレベルを制限しているため、循環参照は回避されます。また、1->n リンク テーブル (エンティティセット) も含まれません。

    private static IDictionary<string, object> JsonFlatten(object data, int maxLevel, int currLevel) {
        var result = new Dictionary<string, object>();
        var myType = data.GetType();
        var myAssembly = myType.Assembly;
        var props = myType.GetProperties();
        foreach (var prop in props) {
            // Remove EntityKey etc.
            if (prop.Name.StartsWith("Entity")) {
                continue;
            }
            if (prop.Name.EndsWith("Reference")) {
                continue;
            }
            // Do not include lookups to linked tables
            Type typeOfProp = prop.PropertyType;
            if (typeOfProp.Name.StartsWith("EntityCollection")) {
                continue;
            }
            // If the type is from my assembly == custom type
            // include it, but flattened
            if (typeOfProp.Assembly == myAssembly) {
                if (currLevel < maxLevel) {
                    result.Add(prop.Name, JsonFlatten(prop.GetValue(data, null), maxLevel, currLevel + 1));
                }
            } else {
                result.Add(prop.Name, prop.GetValue(data, null));
            }
        }

        return result;
    }
    public static IDictionary<string, object> JsonFlatten(this Controller controller, object data, int maxLevel = 2) {
        return JsonFlatten(data, maxLevel, 1);
    }

私の Action メソッドは次のようになります。

    public JsonResult AsJson(int id) {
        var data = Find(id);
        var result = this.JsonFlatten(data);
        return Json(result, JsonRequestBehavior.AllowGet);
    }
于 2010-09-08T09:54:04.637 に答える
2

Entity Framework バージョン 4では、使用可能なオプションがあります: ObjectContextOptions.LazyLoadingEnabled

false に設定すると、「循環参照」の問題を回避できます。ただし、含めるナビゲーション プロパティを明示的に読み込む必要があります。

参照: http://msdn.microsoft.com/en-us/library/bb896272.aspx

于 2011-05-05T21:24:27.690 に答える
1

私の知る限り、オブジェクト参照をシリアル化することはできませんが、コピーのみをシリアル化することはできます。

  1. お客様は、マシンの ID としてマシン参照をシリアル化する必要があります
  2. json コードを逆シリアル化すると、その上で単純な関数を実行して、それらの ID を適切な参照に変換できます。
于 2010-01-04T23:09:06.600 に答える
0

どれが「ルート」オブジェクトかを決定する必要があります。マシンがルートだとすると、顧客はマシンのサブオブジェクトです。マシンをシリアル化すると、JSON のサブオブジェクトとして顧客がシリアル化されます。顧客がシリアル化されると、マシンへの後方参照がシリアル化されません。コードがマシンを逆シリアル化すると、マシンの顧客サブオブジェクトが逆シリアル化され、顧客からマシンへの後方参照が復元されます。

ほとんどのシリアライゼーション ライブラリは、各クラスのデシリアライゼーションの実行方法を変更するための何らかのフックを提供します。そのフックを使用して、マシン クラスの逆シリアル化を変更し、マシンの顧客で後方参照を復元する必要があります。そのフックが正確に何であるかは、使用している JSON ライブラリによって異なります。

于 2010-01-04T23:15:37.063 に答える
0

今週も同じ問題があり、. を要求するインターフェイスを実装する必要があったため、匿名型を使用できませんでしたList<MyType>。ナビゲーション可能性とのすべての関係を示す図を作成した後、相互に保存されているため、 がこの循環参照を引き起こしMyTypeた双方向の関係があることがわかりました。MyObject

MyObjectを実際に知る必要はないと判断しMyType、一方向の関係にした後、この問題は解決されました。

于 2015-08-25T17:29:06.060 に答える