拦截列表总体以在反序列化中分配值 [英] Intercept list population to assign values in deserialization

查看:48
本文介绍了拦截列表总体以在反序列化中分配值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a recursive class (tree hierarchy), that derives from a list, that has the children, and itself, populated from deserialization in JSON.NET.

TLDR版本是我想在其存在的此类的每个级别上,在父级的子级中填充一个变量,而不使用JSON.NET的$ ref变量(在存储到cookie时节省空间) .

The TLDR version is that I want to populate a variable in the children, from the parent, on each level of this class where it exists, without using $ref variables from JSON.NET (saves space when storing to a cookie).

对于昨天追随我的问题的人,这可能看起来像重复的,但事实并非如此.这是相同的代码,但是旧的问题围绕着未触发(已解决)JSON中的setter的问题,答案带来了这个问题(措辞更恰当).

For those that followed my question from yesterday, this may look like a duplicate, but it is not. It is the same code, but the old question revolved around setters in JSON not being fired (resolved), and the answer brought about this question (more aptly worded).

初始呼叫为:

_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":[]}]}]

我想做的事情与此相似:

And what I am trying to do, would be similar to this:

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)?

So the problem being that we have established that deserialization doesn't call the standard Add/Insert methods from a List to populate a list. We know it is creating the list through reflection, but how? Can I intercept the assignment of the child class to the parent class, upon insertion, to assign it a variable in the child class from the parent class (i.e. child.Parent = this)?

我尝试在JSON.NET源代码中四处查找,以在用于填充的集合中找到方法(因为即使进行了反射,它也必须通过调用方法调用来添加它们,对吗?).除非...它正在执行以下操作:

I tried poking around in JSON.NET source to find the method in a collection it is using to populate (because even with reflection, it has to be adding them by invoking a method call, right?). Unless... It is doing something like this:

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

CartItems是从列表派生的类.它填充有CartItem的多个实例.

CartItems is the class derived from list. It is populated with multiple instances of CartItem.

推荐答案

您的问题是,在添加和从列表中删除项目时,您试图将List<T>子类化以维护父/子关系,但是List<T>是由于没有一种相关的方法是虚拟的,因此不能将其设计为以这种方式进行子类化.取而代之的是,您通过public new CartItem Add(CartItem item)隐藏了Add()方法,但是事实证明Json.NET没有调用此替换方法(并且没有理由假设会这样做).

Your problem is that you are trying to subclass List<T> to maintain parent/child relationships as items are added and removed from the list, however List<T> is not designed to be subclassed in this manner as none of the relevant methods are virtual. Instead you are hiding the Add() method via public new CartItem Add(CartItem item), however it turns out that Json.NET is not calling this replacement method (and there is no reason to assume it would).

相反,您应该使用 Collection<T> 是专门为此目的而设计的.从文档:

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

The Collection<T> class provides protected methods that can be used to customize its behavior when adding and removing items, clearing the collection, or setting the value of an existing item.

因此,您的对象模型应类似于以下内容:

Thus your object model should look something like the following:

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; }
}

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

Notice that both CartItem and CartItems have a Parent property. CartItems.Parent is assigned in its constructor. CartItem.Parent is assigned in by the overridden InsertItem, SetItem and RemoveItem methods.

示例小提琴.

另请参见 Collection<T>List<T>和您在界面上应使用什么?.

See also Collection<T> versus List<T> what should you use on your interfaces?.

这篇关于拦截列表总体以在反序列化中分配值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆