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

时间:2017-03-10 17:03:05

标签: c# json.net

我有一个递归类(树层次结构),它派生自一个列表,它具有子节点,并且本身是从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实例。

1 个答案:

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

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

示例fiddle

另见Collection<T> versus List<T> what should you use on your interfaces?