ServiceStack.Text序列化循环引用 [英] ServiceStack.Text serialize circular references

查看:62
本文介绍了ServiceStack.Text序列化循环引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要像这样序列化一个对象图:

I need to serialize an object graph like this:

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

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

因此json仅获得两个实例,但再次正确反序列化.例如.使用元ID或类似的符号.

So that the json only gets two instances, but is deserialized correctly again. E.g. using a meta Id or something similiar.

我知道Json.NET中有一种方法,如下所述: http://note.harajuku-tech.org/serializing-circular-references-with-jsonnet .

I know that there is a way in Json.NET as described here: http://note.harajuku-tech.org/serializing-circular-references-with-jsonnet with meta ids.

ServiceStack.Text Json序列化器中是否有类似功能?

Is there a similiar feature in ServiceStack.Text Json Serializer?

否则,是否可以在ServiceStack中使用Json.NET?如何使用?

Otherwise, is it possible to use Json.NET in ServiceStack and how?

为了清楚起见,我要求提供实例引用,而不仅仅是相同的类型. 例如:

To make it clear, i ask for instance references, not just the same type. An example of this may be:

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

只有2个对象真正"被序列化,其余的则使用$ref属性字段重用. 考虑一个具有子项目集合的对象模型.这些子项目具有对其父对象的反向引用.例如.客户订单.一个客户有多个订单,每个订单都有对其客户的引用. 现在想想如果序列化一位客户会发生什么情况.

There are only 2 objects "really" serialized, the rest is reused using the $ref property field. Think of an object model having a collection of subitems. These subitems have a back-reference to their parent object. E.G. Customer/Order. One customer has multiple orders, each order has a reference to its customer. Now think of what happens, if you serialize one customer.

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

您会得到与该站点名称相似的名称. ;)

And you result in something similiar to this site's name. ;)

我非常喜欢ServiceStack的清晰性,不需要KnownTypeAttribute等.

I really like ServiceStack for its clearity, not to require KnownTypeAttributes etc.

我希望保持它的整洁,而不必在我的业务逻辑pocos中实现自定义加载程序/对象初始化程序.

I would love to keep it that clean, without to implement custom loader/object initializers in my business logic pocos.

推荐答案

我用另一种方法解决了这个问题. 这确实有效,但是稍后在使用具有多个循环引用的更复杂的数据结构时,可能会出现问题.但是目前不需要.

I solved the problem by an alternative way. This actually works, but it could make problems later when using more complex data structures with multiple circular references. But for now there is no need.

我尝试将循环引用功能添加到ServiceStack.Text,但没有找到起点.也许神话可以给我一个提示吗?该功能应该非常简单.

I tried in adding the circular references feature to ServiceStack.Text but found no point to start at it. Maybe mythz could give me a hint? The feature should be really simple to be done.

我需要该功能来对我的数据模型进行序列化,以完全支持NHibernate的合并功能.

I needed that feature for serialization of my data model to fully support NHibernate's merge function.

我遵循myzz的建议,只是忽略IgnoreDataMemberAttribute的属性,这些属性会导致循环引用. 但这还需要在反序列化之后再次对其进行重建,以使合并功能正常工作.

I followed mythz suggestion to just ignore the properties with the IgnoreDataMemberAttribute which cause the circular references. But this also requires to rebuild them after deserialization again, to get the merge feature working.

->这是解决方案,现在遵循我的操作方式:

我从一个简单的原型开始进行测试,这是一个数据模型

I started with a simple prototype to test this, a data model of

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

每个类都是从实体类派生的.

Each class derives from the entity class.

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; }
}

如您所见,OrderOrderDetail对其父对象有一个反向引用,这在序列化时引起了循环引用.可以通过忽略IgnoreDataMemberAttribute的反向引用来解决此问题.

As you can see, Order and OrderDetail have a back reference to it's parent objects, which caused the circular references when serialized. This can be fixed by ignoring the back reference with the IgnoreDataMemberAttribute.

我现在的假设是,在Customer的列表属性Orders中的每个Order子实例都具有对该Customer实例的反向引用.

My assumption now is, that every child instance of Order which is inside Customer's list property Orders has a back reference to this Customer instance.

这就是我重建圆形树的方式:

So this is how i rebuild the circular tree:

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 实体引用(不需要),但我认为它可以轻松扩展.

This code does not work for 1->1 entity references for now (no need yet) but i assume it could be easily extended.

现在,这使我可以在客户端使用POCO类模型,添加/更新/删除子对象,并将整个树发送回服务器. Nhibernate非常聪明,可以确定哪个实体是新的/已更新/已删除.它还仅更新更改的实体,并且也仅更新更改的属性!如果订单被删除,它也将删除所有OrderDetails.

This now allows me to have a POCO class model at client, add/update/remove child objects and send the whole tree back to the server. Nhibernate is clever enough to determine, which entity is new/updated/removed. It also only updates the changed entity and only the changed property as well! It also removes all OrderDetails if an Order is removed.

这就是流利的nhibernate映射,以确保完整性:

Thats the fluent nhibernate mapping for completeness:

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)功能即可正确保存所有内容.

DynamicUpdate() is used to let nhibernate only update the changed properties. You now only need to use the ISession.Merge(customer) function to save everything correctly.

这篇关于ServiceStack.Text序列化循环引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆