我有一个递归类(树层次结构),它派生自一个列表,它具有子节点,并且本身是从JSON.NET中的反序列化填充的。
TLDR版本是我想在子类的子级中填充子级中的变量,而不使用JSON.NET中的$ ref变量(在存储到cookie时节省空间) 。
对于那些昨天跟我提问的人,这可能看起来像是重复,但事实并非如此。它是相同的代码,但旧的问题围绕着JSON中的setter没有被解雇(解决),并且答案带来了这个问题(更恰当的措辞)。
最初的电话会是:
_Items = Cookies.Instance.GetJObject<CartItems>(COOKIE, jsonSetting);
哪个电话:
public T GetJObject<T>(string cookieName, JsonSerializerSettings jset = null)
{
string cookieContent = Get(cookieName);
return JsonConvert.DeserializeObject<T>(cookieContent, jset);
}
自定义转换器类如下:
public class JsonCartConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(CartItem).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get
{
return false;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
var type = obj["t"] != null ? (CARTITEMTYPE)obj["t"].Value<int>() : CARTITEMTYPE.GENERIC;
var item = CartItems.NewByType(type);
serializer.Populate(obj.CreateReader(), item);
return item;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
}
生成的JSON如下:
[{"t":1,"i":1,"n":4,"r":false,"c":[{"t":5,"i":2,"n":4,"r":false,"c":[]}]},{"t":1,"i":3,"n":4,"r":false,"c":[{"t":5,"i":4,"n":4,"r":false,"c":[{"t":4,"i":6,"n":14,"r":false,"c":[]}]},{"t":1,"i":5,"n":15,"r":false,"c":[]}]}]
我想做的事情与此类似:
public class CartItems : List<CartItem>{
public new CartItem Add(CartItem item)
{
item.Parent = this;
base.Add(item);
return item;
}
所以问题在于我们已经确定反序列化不会从List调用标准的Add / Insert方法来填充列表。我们知道它是通过反思创建列表,但是如何?在插入时,我是否可以截取子类的赋值给父类,从子类中为子类中的变量赋值(即child.Parent = this)?
我尝试在JSON.NET源代码中找到它用于填充的集合中的方法(因为即使使用反射,它也必须通过调用方法调用来添加它们,对吧?)。除非......它正在做这样的事情:
CartItems items = new CartItems() { new GenericItem() { }, new GenericItem() { } };
编辑:CartItems是从列表派生的类。它填充了多个CartItem实例。
答案 0 :(得分:1)
您的问题是,您正在尝试子类化List<T>
以维护父/子关系,因为项目已从列表中添加和删除,但List<T>
并非设计为以此方式进行子类化,因为没有相关方法是虚拟的。相反,您通过Add()
隐藏public new CartItem Add(CartItem item)
方法,但事实证明Json.NET没有调用此替换方法(并且没有理由认为它会。)
相反,您应该使用专为此目的而设计的Collection<T>
。来自docs:
Collection<T>
类提供了受保护的方法,可用于在添加和删除项目,清除集合或设置现有项目的值时自定义其行为。
因此,您的对象模型应如下所示:
public class CartItems : Collection<CartItem>
{
public CartItems() : base() { }
public CartItems(CartItem parent) : this()
{
this.Parent = parent;
}
public CartItem Parent { get; private set; }
protected override void RemoveItem(int index)
{
CartItem oldItem = null;
if (index >= 0 && index < Count)
{
oldItem = this[index];
}
base.RemoveItem(index);
}
protected override void InsertItem(int index, CartItem item)
{
base.InsertItem(index, item);
if (item != null)
item.Parent = this;
}
protected override void SetItem(int index, CartItem item)
{
CartItem oldItem = null;
if (index >= 0 && index < Count)
{
oldItem = this[index];
}
base.SetItem(index, item);
if (oldItem != null)
oldItem.Parent = null;
if (item != null)
item.Parent = this;
}
protected override void ClearItems()
{
foreach (var item in this)
if (item != null)
item.Parent = null;
base.ClearItems();
}
}
public class CartItem
{
public CartItem()
{
this.Children = new CartItems(this);
}
public int t { get; set; }
public int i { get; set; }
public int n { get; set; }
public bool r { get; set; }
[JsonProperty("c")]
public CartItems Children { get; private set; }
[JsonIgnore]
public CartItems Parent { get; set; }
}
请注意,CartItem
和CartItems
都有Parent
属性。 CartItems.Parent
在其构造函数中分配。 CartItem.Parent
由被覆盖的InsertItem
,SetItem
和RemoveItem
方法分配。
示例fiddle。
另见Collection<T>
versus List<T>
what should you use on your interfaces?。