Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize to custom list [duplicate]

I created a custom List class that maintains a set of item ids for performance reasons:

public class MyCustomList : List<ItemWithID>
{
    private HashSet<int> itemIDs = new HashSet<int>();

    public MyCustomList()
    {
    }

    [JsonConstructor]
    public MyCustomList(IEnumerable<ItemWithID> collection)
        : base(collection)
    {
        itemIDs = new HashSet<int>(this.Select(i => i.ID));
    }

    public new void Add(ItemWithID item)
    {
        base.Add(item);
        itemIDs.Add(item.ID);
    }

    public new bool Remove(ItemWithID   item)
    {
        var removed = base.Remove(item);
        if (removed)
        {
            itemIDs.Remove(item.ID);
        }
        return removed;
    }

    public bool ContainsID(int id)
    {
        return itemIDs.Contains(id);
    }
}

I want to deserialize this List from a simply JSON array e.g.:

JsonConvert.DeserializeObject<MyCustomList>("[{ID:8},{ID:9}]");

this will cause JSON.NET to call only the empty constructor, so my itemIDs list remains empty. Also the Add method is not called.

How does JSON.NET add the items to the list so I can add logic at that place.

(this is about deserialization without properties that should be persistent in the json string, so the suggested duplicate question has nothing to do with this one)

like image 787
Jan S Avatar asked Nov 03 '15 11:11

Jan S


2 Answers

Solution:

public class MyCustomList : IList<ItemWithID>
{
    private HashSet<int> itemIDs = new HashSet<int>();
    private List<ItemWithID> actualList = new List<ItemWithID>();

    public void Add(ItemWithID item)
    {
        actualList.Add(item);
        itemIDs.Add(item.ID);
    }

    public bool Remove(ItemWithID item)
    {
        var removed = actualList.Remove(item);
        if (removed)
        {
            itemIDs.Remove(item.ID);
        }
        return removed;
    }

    public bool ContainsID(int id)
    {
        return itemIDs.Contains(id);
    }

    public int IndexOf(ItemWithID item)
    {
        return actualList.IndexOf(item);
    }

    public void Insert(int index, ItemWithID item)
    {
        actualList.Insert(index, item);
        itemIDs.Add(item.ID);
    }

    public void RemoveAt(int index)
    {
        itemIDs.Remove(actualList[index].ID);
        actualList.RemoveAt(index);

    }

    public ItemWithID this[int index]
    {
        get
        {
            return actualList[index];
        }
        set
        {
            actualList[index] = value;
            if (!itemIDs.Contains(value.ID))
            {
                itemIDs.Add(value.ID);
            }
        }
    }

    public void Clear()
    {
        actualList.Clear();
        itemIDs.Clear();
    }

    public bool Contains(ItemWithID item)
    {
        return actualList.Contains(item);
    }

    public void CopyTo(ItemWithID[] array, int arrayIndex)
    {
        actualList.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return actualList.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public IEnumerator<ItemWithID> GetEnumerator()
    {
        return actualList.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
like image 77
Oguz Ozgul Avatar answered Sep 18 '22 10:09

Oguz Ozgul


You could deserialize to the form the constructor expects, then call that yourself.

var collection = JsonConvert.DeserializeObject<ItemID[]>("[{ID:8},{ID:9}]");

var aCustomList = new MyCustomList(collection);
like image 39
JonMac1374 Avatar answered Sep 22 '22 10:09

JonMac1374