Каким образом Json. NET десериализует объект по-разному, когда у него есть параметризованный конструктор по сравнению с конструктором по умолчанию?
Json. NET - это потоковая передача десериализатор . По возможности он десериализуется по мере прохождения потока через JSON вместо предварительной загрузки полного JSON в промежуточное представление перед окончательной десериализацией.
Таким образом, при десериализации объекта JSON с помощью конструктора по умолчанию он сначала создает соответствующий объект. Net. Затем он рекурсивно заполняет члены. Net объекта путем потоковой передачи через пары ключ / значение в JSON до конца объекта JSON. Для каждой обнаруженной пары он находит соответствующий член. Net. Если значение является примитивным типом, он десериализует примитив и устанавливает значение. Но если значение является сложным типом (JSON объект или массив), оно при необходимости создает дочерний объект, устанавливает значение обратно в родительский объект, а затем заполняет его рекурсивно, продолжая поток.
Однако , при десериализации объекта с помощью параметризованного конструктора , Json. NET не может использовать этот алгоритм потоковой передачи и вместо этого должен сначала полностью десериализовать объект JSON в промежуточную таблицу десериализации. Net name / значение, сопоставляя каждое значение JSON с соответствующим ему аргументом или свойством конструктора. Net по имени, а затем десериализуя его до типа, объявленного в. Net. Только после этого можно создать объект, передав десериализованные параметры конструктора в конструктор и установив остаток в качестве значений свойств.
Подробнее об этом процессе см.
(Существует третий алгоритм для ISerializable
объектов, которые не применимы в вашем случае.)
Почему мое суррогатное свойство public List<KeyValuePair<Scenario, float>> SerializedDict
не десериализуется правильно при десериализации с помощью конструктора по умолчанию?
Причина объяснена в этот ответ от до Почему все коллекции в моем POCO являются нулевыми при десериализации некоторых действительных json с. NET Newtonsoft. Json компонентом , и возникает из-за особенностей алгоритма Json. NET Populate()
:
Он вызывает метод получения в родительском классе, чтобы получить текущий значение десериализуемого свойства.
Если значение null и если не используется настраиваемый конструктор , i t выделяет экземпляр возвращаемого типа свойства (используя метод JsonContract.DefaultCreator
для типа).
Он вызывает метод установки в родительском элементе для установки выделенного экземпляр обратно в родительский.
Продолжается заполнение экземпляра типа.
Он не возвращает экземпляр во второй раз, после того, как он был заполнен.
Таким образом, установщик для SerializedDict
не вызывается после заполнения списка.
Но когда родительский класс имеет параметризованный конструктор, значение свойства SerializedDict
полностью десериализуется перед созданием его родительского объекта, поэтому сеттер вызывается с полностью заполненным суррогатным списком.
Как создать суррогатную коллекцию свойство, которое работает в обоих сценариях ios?
Вы можете использовать массив вместо списка. Поскольку размер массива нельзя изменить, он должен быть полностью десериализован и заполнен, прежде чем его можно будет вернуть в родительский объект:
public class ReferenceTesting
{
public KeyValuePair<Scenario, float> [] SerializedDict
{
get { return _Dict.ToArray(); }
set { _Dict = value.ToDictionary(x => x.Key, x => x.Value); }
}
// Remainder unchanged
Вы можете сделать свойство массива private
, если хотите, отметив это с помощью [JsonProperty]
.
Между прочим, ваша текущая десериализация создает дублирующиеся объекты Scenario
в коллекциях scenarios
и _Dict
, как показано в демонстрационной скрипте # 1 здесь .
Один из способов исправить это - сериализовать только _Dict
(при условии, что все сценарии ios находятся в словаре). Другой вариант - использовать PreserveReferencesHandling
, например, добавив [JsonObject(IsReference = true)]
к Scenario
:
[JsonObject(IsReference = true)]
public class Scenario
{
// Remainder unchanged
}
Примечания:
Там не является стандартом для сериализации ссылок в JSON. Реализация Json. NET может не совпадать с реализацией других сериализаторов.
PreserveReferencesHandling
не работает для объектов с параметризованными конструкторами (см. здесь для подробностей), поэтому убедитесь, что у Scenario
его нет.
Демо-скрипт # 2 здесь показывает, что все работает правильно с конструктором по умолчанию, и # 3 здесь с параметризованным конструктором.