gpt4 book ai didi

c# - ServiceStack.Text 序列化循环引用

转载 作者:行者123 更新时间:2023-11-30 15:00:50 25 4
gpt4 key购买 nike

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

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带有元 ID。

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 中实现自定义加载器/对象初始化器。

最佳答案

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

我尝试将循环引用功能添加到 ServiceStack.Text,但发现没有必要从它开始。也许 mythz 可以给我一个提示?该功能应该非常容易完成。

我需要该功能来序列化我的数据模型以完全支持 NHibernate 的合并功能。

我遵循了 mythz 的建议,忽略了导致循环引用的 IgnoreDataMemberAttribute 属性。但这也需要在反序列化后再次重建它们,才能使合并功能正常工作。

-> 这就是解决方案,下面是我是如何做到的:

我从一个简单的原型(prototype)开始测试这个,一个数据模型

客户 1->n 订单 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; }
}

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

我现在的假设是,Customer 的列表属性 OrdersOrder 的每个子实例都有对此的反向引用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) 函数即可正确保存所有内容。

关于c# - ServiceStack.Text 序列化循环引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15138872/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com