拦截列表填充以在反序列化中分配值

我有一个递归类(树层次结构),它派生自一个列表,它有子,并且本身是从JSON.NET中的反序列化填充的。

TLDR版本是我想在子类的子级中填充子级中的变量,而不使用来自JSON.NET的$ ref变量(在存储到cookie时节省空间)。

对于那些从昨天开始跟我提问的人,这可能看起来像是重复的,但事实并非如此。 它是相同的代码,但旧的问题围绕着JSON中的setter没有被解雇(解决),并且答案带来了这个问题(更恰当的措辞)。

最初的电话会是:

_Items = Cookies.Instance.GetJObject(COOKIE, jsonSetting); 

哪个电话:

 public T GetJObject(string cookieName, JsonSerializerSettings jset = null) { string cookieContent = Get(cookieName); return JsonConvert.DeserializeObject(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() : 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{ public new CartItem Add(CartItem item) { item.Parent = this; base.Add(item); return item; } 

所以问题在于我们已经确定反序列化不会从List调用标准的Add / Insert方法来填充列表。 我们知道它是通过反思创建列表,但是如何? 在插入时,我是否可以截取子类的赋值给父类,从子类中为子类赋值变量(即child.Parent = this)?

我尝试在JSON.NET源代码中找到它用于填充的集合中的方法(因为即使使用reflection,它也必须通过调用方法调用来添加它们,对吧?)。 除非……它正在做这样的事情:

 CartItems items = new CartItems() { new GenericItem() { }, new GenericItem() { } }; 

编辑:CartItems是从列表派生的类。 它填充了多个CartItem实例。

您的问题是,您尝试将List子类化为在列表中添加和删除项目时维护父/子关系,但是List不是以这种方式设计为子类,因为没有相关的方法是虚拟。 相反,您通过public new CartItem Add(CartItem item)隐藏Add()方法,但事实certificateJson.NET没有调用此替换方法(并且没有理由假设它会)。

相反,您应该使用专门为此目的而设计的Collection 。 来自文档 :

Collection类提供受保护的方法,可用于在添加和删除项目,清除集合或设置现有项目的值时自定义其行为。

因此,您的对象模型应如下所示:

 public class CartItems : Collection { 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; } } 

请注意, CartItemCartItems都具有Parent属性。 CartItems.Parent在其构造函数中分配。 CartItem.Parent由重写的InsertItemSetItemRemoveItem方法指定。

样品小提琴 。

另请参阅CollectionList您应在接口上使用什么? 。