Deserialize object to first element of Array

Jan 27, 2012 at 7:13 PM
Edited Jan 27, 2012 at 7:14 PM

Hi, 

I'm quite new to JSON.NET, and I search the discussions section for an answer but found none. Here is what I want to do : 

I have a json string that comes in, with a field sometimes being an array. 

Example : 

"results" : {}

VS : 

"results" : [{},{}]

How can I map both cases to the following class : 

class response
{
   public List<object> results {get; set;}
}

...and in the first case get the "results" object put in the first element of the List<object> ???

Mar 7, 2012 at 12:29 AM

Hi,

Did you get a solution to this? I'm looking to do the same sort of thing.

my class property is List<string>, but the incoming Json for this property could be either string or List<string> depending on clients selection from checkboxes. With the current setup I get a can not convert string to List<string>

I've been a long day and I might be just being a muppet.


Any help welcomed.

Thanks

Mar 7, 2012 at 12:48 AM
Edited Mar 7, 2012 at 12:48 AM
// usage example (the target json property that we want to parse is "events")
public class MyClass
{

    [JsonProperty("events")]
    private JRaw __events;

    // Use our implementation of IList
    [JsonIgnore()]
    public IList<event> events = new MFList<event>(() => __events)

}


// The wrapper class that does the job
public class MFList : IList
{

	private List list;

	private Func prop;
	public MFList()
	{
	}

	public MFList(Func prop)
	{
		this.prop = prop;
	}

	private void Update()
	{

		if (list == null) {
			dynamic json = prop.Invoke();


			if (json != null) {
				try {
					list = JsonConvert.DeserializeObject>(json.ToString());
				} catch {
					dynamic value = JsonConvert.DeserializeObject(prop.Invoke().ToString());
					list = new List();
					list.Add(value);
				}

			} else {
				list = new List();

			}
		}
	}

	public System.Collections.Generic.IEnumerator GetEnumerator()
	{
		Update();
		return list.GetEnumerator();
	}

	public System.Collections.IEnumerator GetEnumerator1()
	{
		Update();
		return list.GetEnumerator();
	}
	System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
	{
		return GetEnumerator1();
	}

	public void Add(T item)
	{
		list.Add(item);
	}

	public void Clear()
	{
		list.Clear();
	}

	public bool Contains(T item)
	{
		return list.Contains(item);
	}

	public void CopyTo(T[] array, int arrayIndex)
	{
		list.CopyTo(array, arrayIndex);
	}

	public int Count {
		get { return list.Count(); }
	}

	public bool IsReadOnly {
		get { return false; }
	}

	public bool Remove(T item)
	{
		return list.Remove(item);
	}

	public int IndexOf(T item)
	{
		return list.IndexOf(item);
	}

	public void Insert(int index, T item)
	{
		list.Insert(index, item);
	}

	public T this[int index] {
		get { return list[index]; }
		set { list[index] = value; }
	}

	public void RemoveAt(int index)
	{
		list.RemoveAt(index);
	}
}
Mar 7, 2012 at 9:11 AM
Edited Mar 7, 2012 at 9:13 AM

Hey there,

One solution would be to implement ISerializable and handle the creation of the list manually, like so:

 public class MyClass : ISerializable
    {
        protected MyClass(SerializationInfo info, StreamingContext context)
        {
            var data = (JToken)info.GetValue("events", typeof(JToken));

            if (data is JArray)
                events = data.ToObject<List<Event>>();
            else
                events = new List<Event>() { data.ToObject<Event>() };
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            throw new NotImplementedException();
        }

        public IList<Event> events { get; set; }
    }
Though then you'll have to deal with each object manually, the other alternative is to use 
the hidden property as you have above, and have the other property enumerate over it, like 
so:

 public class MyClass 
    {
        [JsonProperty("events")]
        private JRaw __events;

        // Use our implementation of IList
        [JsonIgnore()]
        public IEnumerable<Event> events
        {
            get
            {
                var item = JToken.Parse(__events.ToString());

                if (item is JArray)
                    return item.ToObject<List<Event>>();

                return new[] { item.ToObject<Event>() };
            }
        }
    }

Phil
Coordinator
Mar 7, 2012 at 10:31 AM

http://stackoverflow.com/questions/5224697/deserializing-json-when-sometimes-array-and-sometimes-object

Mar 7, 2012 at 8:07 PM

thanks to everyone for the quick answer.

I now have it working with Phil's alternative option and moved it into a simple method.

You have both saved me yet another long night.. Please free to have a beer from me.