Prabhanjan Kumar Mahapatra Prabhanjan Kumar Mahapatra - 3 months ago 8
C# Question

JsonConvert.Deserializer indexing issues

While playing around Stack collection in C# I encountered the following issue. Exactly I am not sure why it is happening. Please put some light on the reason and alternative to the solution.

Problem -

A class having Stack as property. For example name that class as Progress. T is of class type Item.

Now whenever the user makes any progress we will be storing in stack. And if user leaves in between, then next time we will peek the item from the stack so from that stage. Below code snippet will give an idea of what is being tried...

using static System.Console;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace StackCollection
{
class Program
{
static void Main(string[] args)
{
Progress progress = new Progress();

progress.Items.Push(new Item { PlanID = null, PlanName = "Plan A" });

var jsonString = JsonConvert.SerializeObject(progress);
var temp = JsonConvert.DeserializeObject<Progress>(jsonString);

temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" });

jsonString = JsonConvert.SerializeObject(temp);
temp = JsonConvert.DeserializeObject<Progress>(jsonString);

temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" });

jsonString = JsonConvert.SerializeObject(temp);
temp = JsonConvert.DeserializeObject<Progress>(jsonString);

WriteLine(temp.Items.Peek().PlanName);

ReadLine();
}
}

class Progress
{
public Stack<Item> Items { get; set; }

public Progress()
{
Items = new Stack<Item>();
}
}

class Item
{
public string PlanID { get; set; }

public string PlanName { get; set; }
}
}


now the line -

WriteLine(temp.Items.Peek().PlanName);


should return


Plan C


but it is returning


Plan B


So, why the index is being changed, any clue or pointer will be helpful.

Answer

It seems like the queue is being serialized as a List. The problem is that this does not preserve the proper order when deconstructing the stack (the items are actually pushed in the reverse order). Here's a quick workaround to this issue:

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;

namespace StackCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            Progress progress = new Progress();

            progress.Items.Push(new Item { PlanID = null, PlanName = "Plan A" });

            var jsonString = JsonConvert.SerializeObject(progress);
            var temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" });

            jsonString = JsonConvert.SerializeObject(temp);
            temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" });

            jsonString = JsonConvert.SerializeObject(temp);
            temp = JsonConvert.DeserializeObject<Progress>(jsonString);

            WriteLine(temp.Items.Peek().PlanName);

            ReadLine();
        }
    }

    class Progress
    {
        [JsonIgnore]
        public Stack<Item> Items { get; set; }

        public List<Item> ItemList { get; set; }

        [OnSerializing]
        internal void OnSerializing(StreamingContext context)
        {
            ItemList = Items?.ToList();
        }

        [OnDeserialized]
        internal void OnDeserialized(StreamingContext context)
        {
            ItemList?.Reverse();
            Items = new Stack<Item>(ItemList ?? Enumerable.Empty<Item>());
        }

        public Progress()
        {
            Items = new Stack<Item>();
        }
    }

    class Item
    {
        public string PlanID { get; set; }

        public string PlanName { get; set; }
    }
}