Member 13801974 Ответов: 1

Десериализатор JSON не может создать и заполнить объект


Привет,

Я долго спорил с десериализацией определенного фрагмента json в объект, который развертывает интерфейс IEnumerable, чтобы я мог перебирать его список и использовать некоторые его атрибуты в качестве параметров в SQL-запросе.

Я уже получил несколько ответов, которые не решили проблему. Вы можете посмотреть их здесь:

c# - ошибка десериализации JSON в объект IEnumerable<T> - переполнение стека[^]

Я опубликую пример строки json, мои классы для обработки десериализации, ошибку, которую я получаю при попытке сделать это, и то, как новый объект будет использоваться, когда он будет правильно десериализован.

Что я уже пробовал:

//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);
            }
        }


Вот ошибка, которую я получаю:

Цитата:
{"Невозможно создать и заполнить список типа AppointmentReasonList. Путь "AppointmentReasonList", строка 1, позиция 59."}


Я был бы рад любой обратной связи, которую вы можете предоставить.

1 Ответов

Рейтинг:
4

Eric Lynch

У тебя самые большие проблемы с этим AppointmentReasonList и ReasonCodeList занятия. В вашем документе JSON они представлены в виде коллекций. Однако, поскольку оба класса просто реализуют IEnumerable{T}, нет никакого способа, которым де-сериализатор может добавить элементы в коллекцию.

Рассмотрим либо наследование непосредственно от List{T} класс или реализация ICollection{T} или IList{T}.

В моих экспериментах оба они разрешили проблему, которую вы заметили.

Отдельно, один из ваших foreach циклы недопустимы, AppointmentReason не перечисли, ты наверно имеешь ввиду ApptReason.ReasonCodeList.

Итак, просто чтобы уточнить, ниже приведены рекомендуемые исправления. Каждый из них был протестирован в VS 2017 (с закомментированным вызовом отсутствующего кода AddInterfaceRMReasonCode).

Это рекомендуемое исправление для foreach петля:

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


Это самое простое решение проблем с вашими классами:

public class AppointmentReasonList : List<AppointmentReason>
{
}

public class ReasonCodeList : List<ReasonCode>
{
}


В случае, если вы просто предоставляли некоторые клипы кода и у вас есть причина не использовать самое простое исправление, ниже приведено исправление, которое ближе всего к вашему исходному коду:

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();
	}
}


Удачи, надеюсь, это поможет.

-Эрик.


Member 13801974

Эрик, большое тебе спасибо за ответ. Вы помогли мне решить проблему, которая отняла у меня несколько недель времени. То, что вы предложили в качестве ответа, отлично работает, хотя мне все еще немного неясно, почему десериализатор JSON не может заполнять/ добавлять объекты IEnumerable list, но может делать это для объектов List<t> или ICollection<t>.

Я сделал быстрый поиск в google по этой теме, но не смог найти ничего, чтобы всесторонне объяснить ее. Не могли бы Вы уточнить или указать мне на ресурс, где я могу прочитать больше об этом?

Кроме того, причина, по которой мои классы были структурированы с помощью объекта list IEnumerable<t> в качестве свойства вместо того, чтобы быть самими объектами IEnumerable<t>, может быть объяснена по следующей ссылке:

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

Еще раз большое спасибо за вашу помощь. Ты был просто находкой.

Eric Lynch

Я счастлив, что смог помочь. Интерфейс IEnumerable<t> предоставляет только два метода, различные версии GetEnumerator. Очевидно, что они не могут быть использованы для добавления элемента в коллекцию. Как ICollection<t>, так и IList<t> включают метод Add. Этот метод позволяет де-сериализатору добавить элемент.

Возможно, вы ожидали, что де-десиализатор будет достаточно "умен", чтобы вызвать метод Add в свойстве (например, AppointReasonLi)? Если бы де-сериализатор сделал это, это было бы на самом деле немного глупо.

В принципе, у вас есть несоответствие между вашим документом JSON и моделью данных в исходном коде. Возможны два решения: 1) исправить документ JSON или 2) исправить исходный код.

Я предложил исправить исходный код, потому что это то, что я предполагаю, что вы хотели.

Альтернативой было бы добавить промежуточный объект JSON. Например (не тестировалось), я думаю, что это будет что-то вроде AppartmentReasonList { AppartmentReasonLi [ { ClassCode : "blah", ... }, ... ] }.

Как бы то ни было, я тоже немного боролся, когда впервые начал играть с более сложными десериализаторами. В моем случае это был сначала XML-де-сериализатор.

В моем случае это помогло действительно подумать о том, как был реализован де-сериализатор. Я даже немного поиграл с рефлексией, реализуя свою собственную глупую версию, чтобы лучше понять ее.

Надеюсь, это поможет. Удачи.

Member 13801974

Эрик, да, твой комментарий был очень проницательным. Спасибо, что вы нашли время объяснить мне такие вещи, это дает мне хорошее направление, чтобы идти вперед и узнать больше.