3

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

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

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

这样 json 只会获得两个实例,但会再次正确反序列化。例如,使用元 ID 或类似的东西。

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

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 客户/订单。一个客户有多个订单,每个订单都有一个对其客户的引用。现在想想如果你序列化一个客户会发生什么。

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

并且您的结果与该站点的名称相似。;)

我真的很喜欢 ServiceStack 的清晰性,而不是需要KnownTypeAttributes 等。

我很想保持它的干净,而不是在我的业务逻辑 pocos 中实现自定义加载器/对象初始化器。

4

3 回答 3

4

我用另一种方式解决了这个问题。这实际上是可行的,但是当使用具有多个循环引用的更复杂的数据结构时,它可能会在以后产生问题。但目前没有必要。

我尝试向其中添加循环引用功能,ServiceStack.Text但没有发现从它开始的意义。也许mythz可以给我一个提示?该功能应该非常简单。

我需要该功能来序列化我的数据模型以完全支持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

我现在的假设是,其中的每个子实例OrderinsideCustomer的 list 属性Orders都有一个对该Customer实例的反向引用。

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

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足够聪明地确定哪个实体是新的/更新的/删除的。它也只更新更改的实体和更改的属性!如果订单被删除,它也会删除所有 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 回答