Click here to Skip to main content
15,890,506 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello,

I've been wrangling with deserializing a certain json snippet into an object that deploys the IEnumerable interface so I can loop through its list and use some of its attributes as parameters in a SQL query.

I already received a few answers that didn't solve the problem. You can view them here:

c# - Error Deserializing JSON into IEnumerable<T> Object - Stack Overflow[^]

I will post an example of the json string, my classes to handle the deserialization, the error I'm receiving when I attempt to do so, and how the new object will be being used when it is correctly deserialized.

What I have tried:

//Json Example:

{
    	"ErrorCode":""
    	,"ErrorMessage":""
    	,"AppointmentReasonList":[
    		{
    			"ClassCode":"851"
    			,"ClassDescription":"newpat"
    			,"Active":true
    			,"IsPatientRelated":true
    			,"ReasonCodeList":[
    				{
    					"Code":"851"
    					,"Description":"Emergency New Patient Visit"
    					,"Duration":15
    					,"Active":true
    				}
    				,{
    					"Code":"BH NEW"
    					,"Description":"BH NEW"
    					,"Duration":15
    					,"Active":true
    				}
    							]
    		}
    		,{
    			"ClassCode":"ANE"
    			,"ClassDescription":"Anesthesia"
    			,"Active":true
    			,"IsPatientRelated":true
    			,"ReasonCodeList":[
    				{
    					"Code":"123456"
    					,"Description":"This is only a test"
    					,"Duration":15
    					,"Active":true
    				}
    								]
    		}					
    							]
    }

 //Classes to handle JSON structure

        public class AppointmentReasonResponse
        {
            public string ErrorCode { get; set; }
            public string ErrorMessage { get; set; }
            public AppointmentReasonList AppointmentReasonList { get; set; }
        }

        public class AppointmentReasonList : IEnumerable<AppointmentReason>
        {

            public List<AppointmentReason> AppointmentReasonLi { get; set; }

            IEnumerator<AppointmentReason> IEnumerable<AppointmentReason>.GetEnumerator()
            {
                foreach (AppointmentReason ApptReason in AppointmentReasonLi)
                {
                    yield return ApptReason;
                }
            }

            public IEnumerator<AppointmentReason> GetEnumerator()
            {
                return AppointmentReasonLi.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
        }

        public class AppointmentReason 
        {

            public string ClassCode { get; set; }
            public string ClassDescription { get; set; }
            public bool Active { get; set; }
            public bool IsPatientRelated { get; set; }
            public ReasonCodeList ReasonCodeList { get; set; }
        }

        public class ReasonCodeList : IEnumerable<ReasonCode>
        {
            public List<ReasonCode> ReasonCodeLi { get; set; }

            IEnumerator<ReasonCode> IEnumerable<ReasonCode>.GetEnumerator()
            {
                // Return the array object's IEnumerator.
                foreach (ReasonCode Reason in ReasonCodeLi)
                {
                    yield return Reason;
                }
            }

            public IEnumerator<ReasonCode> GetEnumerator()
            {
                // Return the array object's IEnumerator.
                return ReasonCodeLi.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                // call the generic version of the method
                return this.GetEnumerator();
            }
        }

        public class ReasonCode
        {
            public string Code { get; set; }
            public string Description { get; set; }
            public int Duration { get; set; }
            public bool Active { get; set; }
        }

//use of resulting object

jarray = JsonConvert.DeserializeObject<AppointmentReasonResponse>(json);
        foreach (AppointmentReason ApptReason in jarray.AppointmentReasonList)
        {
            foreach (ReasonCode Reason in ApptReason)
            {
                AddInterfacePMReasonCode(PracticeID, Reason.Code, Reason.Description);
            }
        }


Here is the error I'm getting:

Quote:
{"Cannot create and populate list type AppointmentReasonList. Path 'AppointmentReasonList', line 1, position 59."}


Would love any help feedback you can provide.
Posted
Updated 28-Apr-18 10:11am

1 solution

You're biggest problems are with the AppointmentReasonList and ReasonCodeList classes. In your JSON document, these are represented as collections. However, since both classes simply implement IEnumerable{T}, there is no way the de-serializer can add elements to the collection.

Consider either inheriting directly from the List{T} class or implementing ICollection{T} or IList{T}.

In my experiments, both of these resolved the issue you note.

Separately, one of your foreach loops is not valid, AppointmentReason is not enumerable, you probably mean ApptReason.ReasonCodeList.

So, just to clarify, below are the recommended fixes. Each has been tested in VS 2017 (with the call to the missing AddInterfaceRMReasonCode commented out).

This is the recommended fix to the foreach loop:

C#
foreach (AppointmentReason ApptReason in jarray.AppointmentReasonList)
{
  foreach (ReasonCode Reason in ApptReason.ReasonCodeList)
  {
    AddInterfacePMReasonCode(PracticeID, Reason.Code, Reason.Description);
  }
}


This is the easiest fix to the problems with your classes:

C#
public class AppointmentReasonList : List<AppointmentReason>
{
}

public class ReasonCodeList : List<ReasonCode>
{
}


In case you were simply providing some code clips, and have a reason for not using the simplest fix, below is a fix that is closest to your original code:

C#
public class AppointmentReasonList : ICollection<AppointmentReason>
{
	public AppointmentReasonList()
	{
		AppointmentReasonLi = new List<AppointmentReason>();
	}

	public List<AppointmentReason> AppointmentReasonLi { get; set; }

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

	public bool IsReadOnly
	{
		get
		{
			return false;
		}
	}

	public void Add(AppointmentReason item)
	{
		AppointmentReasonLi.Add(item);
	}

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

	public bool Contains(AppointmentReason item)
	{
		return AppointmentReasonLi.Contains(item);
	}

	public void CopyTo(AppointmentReason[] array, int arrayIndex)
	{
		AppointmentReasonLi.CopyTo(array, arrayIndex);
	}

	public IEnumerator<AppointmentReason> GetEnumerator()
	{
		return AppointmentReasonLi.GetEnumerator();
	}

	public bool Remove(AppointmentReason item)
	{
		return AppointmentReasonLi.Remove(item);
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}
}

public class ReasonCodeList : ICollection<ReasonCode>
{
	public ReasonCodeList()
	{
		ReasonCodeLi = new List<ReasonCode>();
	}

	public List<ReasonCode> ReasonCodeLi { get; set; }

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

	public bool IsReadOnly
	{
		get
		{
			return false;
		}
	}

	public void Add(ReasonCode item)
	{
		ReasonCodeLi.Add(item);
	}

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

	public bool Contains(ReasonCode item)
	{
		return ReasonCodeLi.Contains(item);
	}


	public void CopyTo(ReasonCode[] array, int arrayIndex)
	{
		ReasonCodeLi.CopyTo(array, arrayIndex);
	}

	public IEnumerator<ReasonCode> GetEnumerator()
	{
		return ReasonCodeLi.GetEnumerator();
	}


	public bool Remove(ReasonCode item)
	{
		return ReasonCodeLi.Remove(item);
	}


	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}
}


Good luck, hope this helps.

-Eric.
 
Share this answer
 
v2
Comments
Member 13801974 30-Apr-18 11:10am    
Eric, thank you so much for your answer. You have helped me resolve an issue that has taken weeks of my time. What you suggested as an answer works great, although I'm still a little unclear as to why the JSON deserializer is unable to populate/ add to IEnumerable list objects but is able to do so for List<t>, or ICollection<t> objects.

I did a quick google search on the topic but couldn't find anything to comprehensively explain it. Would you mind elaborating or pointing me to a resource where I can read more about this?

Also, the reason my classes were structured with the list IEnumerable<t> object as a property instead of being the IEnumerable<t> objects themselves outright can be explained at the following link:

https://stackoverflow.com/questions/34363139/error-when-deserializing-json-to-object

Again, thank you so much for your help. You've been a godsend.
Eric Lynch 30-Apr-18 13:38pm    
I am happy I could help. The IEnumerable<t> interface only provides two methods, different versions of GetEnumerator. These obviously cannot be used to add an item to a collection. Both ICollection<t> and IList<t> include an Add method. This method allows the de-serializer to add an item.

Perhaps you expected the de-desializer to be "smart" enough to call the Add method on the property (e.g. AppointReasonLi)? If the de-serializer did this, it would actually be a bit dumb.

Basically, you have a mismatch between your JSON document and the data model in your source code. Two solutions are possible: 1) fix the JSON document or 2) fix the source code.

I suggested fixing the source code, because that is what I am guessing you wanted.

The alternative would be to add an intervening JSON object. For example (not tested), I think it would end up being something like AppartmentReasonList { AppartmentReasonLi [ { ClassCode : "blah", ... }, ... ] }.

For what its worth, I struggled a little as well, way back when I first starting playing with the more complex de-serializers. In my case, it was the XML de-serializer first.

In my case, it helped to really think about how the de-serializer was implemented. I even played a little with reflection, implementing my own dumb version, to better understand it.

I hope this helps. Good luck.
Member 13801974 30-Apr-18 14:33pm    
Eric, yes your comment was very insightful. Appreciate you taking the time to explain this sort of thing, it gives me good direction to go forward with and learn more.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900