ServiceStack.Text序列化循环引用
我需要像这样序列化一个对象图:
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.Text
Json Serializer中是否有类似的function?
否则,是否可以在ServiceStack
使用Json.NET以及如何使用?
编辑:
为了说清楚,我要求提供实例引用,而不仅仅是相同类型。 一个例子可能是:
[ { "$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的清晰度,不需要KnownTypeAttribute
等。
我希望保持它干净,而不是在我的业务逻辑pocos中实现自定义加载器/对象初始化器。
我通过另一种方式解决了这个问题。 这实际上是有效的,但是当使用具有多个循环引用的更复杂的数据结构时,它可能会出现问题。 但目前没有必要。
我尝试将循环引用function添加到ServiceStack.Text
但发现没有必要从它开始。 也许mythz可以给我一个提示? 该function应该非常简单。
我需要这个function来序列化我的数据模型,以完全支持NHibernate
的合并function。
我遵循mythz建议,忽略了导致循环引用的IgnoreDataMemberAttribute
的属性。 但是这也需要在反序列化之后重建它们,以使合并function正常工作。
– >这是解决方案,现在遵循我的方式:
我开始用一个简单的原型来测试这个,一个数据模型
Customer
1-> n Orders
1-> n Orders
。
每个类都派生自实体类。
public class Customer : Entity { public virtual string Name { get; set; } public virtual string City { get; set; } public virtual IList Orders { get; set; } } public class Order : Entity { public virtual DateTime OrderDate { get; set; } public virtual IList 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
忽略后引用来IgnoreDataMemberAttribute
。
我现在的假设是,在Customer
的list属性Orders
的Order
每个子实例都有一个对这个Customer
实例的反向引用。
所以这就是我重建圆形树的方式:
public static class SerializationExtensions { public static void UpdateChildReferences(this object input) { var hashDictionary = new Dictionary(); 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 { 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 { 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(x => x.Customer, "CUSTOMER_ID"); DynamicUpdate(); } } public class OrderDetailMap : ClassMap { 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(x => x.Order, "ORDER_ID"); DynamicUpdate(); } }
DynamicUpdate()用于让nhibernate仅更新已更改的属性。 您现在只需要使用ISession.Merge(customer)
function来正确保存所有内容。
ServiceStack默认支持循环引用。
为什么不在发布之前先自己尝试validation是否存在实际问题? 这比创建一个新问题并要求其他人这样做更省力。
按照你的例子:
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(); fromJson.PrintDump();
将转储内容:
{ Name: A1, Link1: { Name: B1, Link2: { Name: A2 } } }
如果有人需要能够使用循环序列化对象图,JSON.NET确实支持它:
new JsonSerializer { PreserveReferencesHandling = PreserveReferencesHandling.Objects };