3

次のようにオブジェクト グラフをシリアル化する必要があります。

public class A
{
     public B Link1 {get;set;}
}

public class B
{
     public A Link2 {get;set;}
}

そのため、json は 2 つのインスタンスしか取得できませんが、再び正しく逆シリアル化されます。たとえば、メタ ID または類似のものを使用します。

ここで説明されているように、Json.NET に方法があることを知っています: http://note.harajuku-tech.org/serializing-circular-references-with-jsonnetとメタ ID。

ServiceStack.TextJson Serializerに同様の機能はありますか?

それ以外の場合、Json.NETServiceStackをどのように使用できますか?

編集:

明確にするために、同じタイプだけでなく、インスタンス参照を求めます。これの例は次のとおりです。

[
    {
        "$id": "1",
        "BroId": 0,
        "Name": "John",
        "Bros": [
            {
                "$id": "2",
                "BroId": 0,
                "Name": "Jared",
                "Bros": [
                    {
                        "$ref": "1"
                    }
                ]
            }
        ]
    },
    {
        "$ref": "2"
    }
]

「実際に」シリアル化されたオブジェクトは 2 つだけで、残りは$refプロパティ フィールドを使用して再利用されます。サブアイテムのコレクションを持つオブジェクト モデルを考えてみてください。これらのサブアイテムには、親オブジェクトへの後方参照があります。EG 顧客/注文。1 人の顧客には複数の注文があり、各注文にはその顧客への参照があります。1 人の顧客をシリアル化するとどうなるか考えてみてください。

Customer
 -> Order
  -> Customer
   -> Order
    -> ...

そして、このサイトの名前に似たものになります。;)

KnownTypeAttributes などを必要としないため、その明確さのために ServiceStack が本当に好きです。

ビジネス ロジック pocos にカスタム ローダー/オブジェクト初期化子を実装せずに、きれいに保ちたいと思います。

4

3 に答える 3

4

別の方法で問題を解決しました。これは実際には機能しますが、後で複数の循環参照を持つより複雑なデータ構造を使用するときに問題が発生する可能性があります。しかし、今のところ必要ありません。

循環参照機能を追加しようとしましたServiceStack.Textが、開始するポイントが見つかりませんでした。神話がヒントになるかも?この機能は、非常に簡単に実行できる必要があります。

NHibernateのマージ機能を完全にサポートするには、データ モデルのシリアル化にその機能が必要でした。

IgnoreDataMemberAttribute循環参照の原因となるプロパティを無視するという神話の提案に従いました。ただし、これには、マージ機能を機能させるために、逆シリアル化後に再構築する必要もあります。

->これが解決策です。今は私がやった方法に従います:

これをテストするための単純なプロトタイプから始めました。

Customer 1->n Orders 1->n OrderDetail .

各クラスはエンティティ クラスから派生します。

public class Customer : Entity
{
    public virtual string Name { get; set; }
    public virtual string City { get; set; }
    public virtual IList<Order> Orders { get; set; }
}

public class Order : Entity
{
    public virtual DateTime OrderDate { get; set; }
    public virtual IList<OrderDetail> OrderDetails { get; set; }
    [IgnoreDataMember]
    public virtual Customer Customer { get; set; }
}

public class OrderDetail : Entity
{
    public virtual string ProductName { get; set; }
    public virtual int Amount { get; set; }
    [IgnoreDataMember]
    public virtual Order Order{ get; set; }
}

ご覧のとおり、親オブジェクトへの後方参照がOrderありOrderDetail、シリアル化時に循環参照が発生しました。これは、 で後方参照を無視することで修正できますIgnoreDataMemberAttribute

私の仮定は、の list プロパティOrder内にあるすべての子インスタンスが、このインスタンスへの後方参照を持っているということです。CustomerOrdersCustomer

だから、これは私が循環ツリーを再構築する方法です:

public static class SerializationExtensions
{
    public static void UpdateChildReferences(this object input)
    {
        var hashDictionary = new Dictionary<int, object>();
        hashDictionary.Add(input.GetHashCode(), input);

        var props = input.GetType().GetProperties();
        foreach (var propertyInfo in props)
        {
            if (propertyInfo.PropertyType.GetInterfaces()
                .Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
            {

                var instanceTypesInList = propertyInfo.PropertyType.GetGenericArguments();
                if(instanceTypesInList.Length != 1)
                    continue;

                if (instanceTypesInList[0].IsSubclassOf(typeof(Entity)))
                {
                    var list = (IList)propertyInfo.GetValue(input, null);
                    foreach (object t in list)
                    {
                        UpdateReferenceToParent(input, t);
                        UpdateChildReferences(t);
                    }
                }
            }
        }
    }

    private static void UpdateReferenceToParent(object parent, object item)
    {
        var props = item.GetType().GetProperties();
        var result = props.FirstOrDefault(x => x.PropertyType == parent.GetType());

        if (result != null)
            result.SetValue(item, parent, null);
    }
}

このコードは、現時点では1->1エンティティ参照では機能しません (まだ必要ありません) が、簡単に拡張できると思います。

これにより、クライアントで POCO クラス モデルを作成し、子オブジェクトを追加/更新/削除して、ツリー全体をサーバーに送り返すことができるようになりました。Nhibernateどのエンティティが新規/更新/削除されたかを判断するのに十分賢いです。また、変更されたエンティティのみを更新し、変更されたプロパティのみも更新します! Order が削除された場合は、すべての OrderDetails も削除されます。

完全を期すために流暢な nhibernate マッピングを次に示します。

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Schema("YOURSCHEMA");
        Table("CUSTOMER");
        Id(x => x.Id, "ID").GeneratedBy.Assigned();
        Map(x => x.Name, "NAM");
        Map(x => x.City, "CITY");
        HasMany(x => x.Orders)
            .KeyColumn("CUSTOMER_ID")
            .Not.LazyLoad()
            .Inverse()
            .Cascade.AllDeleteOrphan();


        DynamicUpdate();
    }
}

public class OrderMap : ClassMap<Order>
{
    public OrderMap()
    {
        Schema("YOURSCHEMA");
        Table("CUSTOMER_ORDER");
        Id(x => x.Id, "ID").GeneratedBy.Assigned();
        Map(x => x.OrderDate, "ORDER_DATE");
        HasMany(x => x.OrderDetails)
            .KeyColumn("ORDER_ID")
            .Not.LazyLoad()
            .Inverse()
            .Cascade.AllDeleteOrphan();

        References<Customer>(x => x.Customer, "CUSTOMER_ID");
        DynamicUpdate();
    }
}

public class OrderDetailMap : ClassMap<OrderDetail>
{
    public OrderDetailMap()
    {
        Schema("YOURSCHEMA");
        Table("ORDER_DETAIL");
        Id(x => x.Id, "ID").GeneratedBy.Assigned();
        Map(x => x.ProductName, "PRODUCT_NAME");
        Map(x => x.Amount, "AMOUNT");

        References<Order>(x => x.Order, "ORDER_ID");
        DynamicUpdate();
    }
}

DynamicUpdate() は、nhibernate のみが変更されたプロパティを更新できるようにするために使用されます。関数を使用するだけで、ISession.Merge(customer)すべてを正しく保存できます。

于 2013-03-14T10:19:47.123 に答える
1

オブジェクト グラフをサイクルでシリアル化できる必要がある場合、JSON.NET はそれをサポートします。

new JsonSerializer
{
    PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
于 2015-04-03T15:46:21.833 に答える
0

ServiceStack はデフォルトで循環参照をサポートしています。

投稿する前に実際の問題があるかどうかを確認するために、まずこれを自分で試してみませんか? これは、新しい質問を作成して他の人に依頼するよりも手間がかかりません。

あなたの例に従ってください:

public class A
{
    public string Name { get; set; }
    public B Link1 { get; set; }
}

public class B
{
    public string Name { get; set; }
    public A Link2 { get; set; }
}

var dto = new A { 
   Name = "A1", 
   Link1 = new B { Name = "B1", Link2 = new A { Name = "A2" } } 
};
dto.ToJson().Print();

JSON 文字列を出力します。

{"Name":"A1","Link1":{"Name":"B1","Link2":{"Name":"A2"}}}

それを JSON にシリアル化し、次のように再び逆シリアル化します。

var fromJson = dto.ToJson().FromJson<A>();
fromJson.PrintDump();

内容をダンプします:

{
    Name: A1,
    Link1: 
    {
        Name: B1,
        Link2: 
        {
            Name: A2
        }
    }
}
于 2013-02-28T19:20:24.780 に答える