我需要像这样序列化对象图:
public class A
{
public B Link1 {get;set;}
}
public class B
{
public A Link2 {get;set;}
}
这样json只获得两个实例,但是再次正确反序列化。例如。使用元ID或类似的东西。
我知道Json.NET中有一种方法,如下所述:带有元ID的http://note.harajuku-tech.org/serializing-circular-references-with-jsonnet。
ServiceStack.Text
Json Serializer中是否有类似的功能?
否则,是否可以在ServiceStack
中使用Json.NET以及如何使用?
修改
为了说清楚,我要求提供实例引用,而不仅仅是相同类型。 这方面的一个例子可能是:
[
{
"$id": "1",
"BroId": 0,
"Name": "John",
"Bros": [
{
"$id": "2",
"BroId": 0,
"Name": "Jared",
"Bros": [
{
"$ref": "1"
}
]
}
]
},
{
"$ref": "2"
}
]
只有2个对象“真正”序列化,其余对象使用$ref
属性字段重用。
想象一下具有子项集合的对象模型。这些子项具有对其父对象的反向引用。例如。顾客订单。一个客户有多个订单,每个订单都有一个对其客户的引用。
现在想想如果你序列化一个客户会发生什么。
Customer
-> Order
-> Customer
-> Order
-> ...
您会得到与此网站名称类似的内容。 ;)
我非常喜欢ServiceStack的清晰度,而不是要求KnownTypeAttribute
等。
我希望保持干净,不要在我的业务逻辑pocos中实现自定义加载器/对象初始化器。
答案 0 :(得分:4)
我通过另一种方式解决了这个问题。 这实际上有效,但是当使用带有多个循环引用的更复杂的数据结构时,它可能会出现问题。但是现在没有必要。
我尝试将循环引用功能添加到ServiceStack.Text
但是没有任何意义从此开始。也许mythz可以给我一个提示?该功能应该非常简单。
我需要该功能来序列化我的数据模型,以完全支持NHibernate
的合并功能。
我遵循mythz建议忽略导致循环引用的IgnoreDataMemberAttribute
属性。
但是这也需要在反序列化之后重建它们,以使合并功能正常工作。
<强> - &GT;这是解决方案,现在遵循我的方式:
我从一个简单的原型开始测试,这是一个
的数据模型 Customer
1-&gt; n Orders
1-&gt; 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
忽略后引用来解决。
我现在的假设是,位于Order
列表属性Customer
内的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&gt; 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)
功能正确保存所有内容。
答案 1 :(得分:1)
如果有人需要能够使用循环序列化对象图,JSON.NET确实支持它:
new JsonSerializer
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
答案 2 :(得分: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
}
}
}