Я пытаюсь расширить приведенный здесь пример JSON.net http://james.newtonking.com/projects/json/help/CustomCreationConverter.html
У меня есть другой подкласс, производный от базового класса / интерфейса
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
List<Person> people = new List<Person>
{
new Employee(),
new Employee(),
new Artist(),
};
Как я могу десериализовать следующий Json обратно в список <Person>
[
{
"Department": "Department1",
"JobTitle": "JobTitle1",
"FirstName": "FirstName1",
"LastName": "LastName1"
},
{
"Department": "Department2",
"JobTitle": "JobTitle2",
"FirstName": "FirstName2",
"LastName": "LastName2"
},
{
"Skill": "Painter",
"FirstName": "FirstName3",
"LastName": "LastName3"
}
]
Я не хочу использовать TypeNameHandling JsonSerializerSettings. Я специально ищу пользовательскую реализацию JsonConverter для этого. Документация и примеры вокруг этого довольно редки в сети. Кажется, я не могу правильно понять переопределенную реализацию метода ReadJson () в JsonConverter.
c#
json
json.net
deserialization
Snakebyte
источник
источник
Ответы:
Используя стандарт
CustomCreationConverter
, я изо всех сил пытался выработать правильный тип (Person
илиEmployee
), потому что для определения этого вам нужно проанализировать JSON, и нет встроенного способа сделать это с помощьюCreate
метода.Я нашел ветку обсуждения, касающуюся преобразования типов, и, как оказалось, дал ответ. Вот ссылка: Тип преобразования .
Требуется создать подкласс
JsonConverter
, переопределитьReadJson
метод и создать новый абстрактныйCreate
метод, который принимает aJObject
.Переопределенный
ReadJson
метод создаетJObject
и вызываетCreate
метод (реализованный нашим производным классом преобразователя), передаваяJObject
экземпляр.JObject
Затем этот экземпляр можно проанализировать, чтобы определить правильный тип, проверив наличие определенных полей.пример
источник
JsonReader
созданный вReadJson
методе не наследует какие - либо из параметров конфигурации исходного читателя (Culture
,DateParseHandling
,DateTimeZoneHandling
,FloatParseHandling
и т.д. ...). Эти значения должны быть скопированы перед использованием новогоJsonReader
вserializer.Populate()
.JsonConverter
есть свойство с именемCanRead
andCanWrite
. Если вам не нужна пользовательскаяWriteJson
реализация, достаточно позволитьCanWrite
returnFALSE
. Затем система вернется к поведению по умолчанию. @jdavies: Пожалуйста, добавьте это к своему ответу. В противном случае произойдет сбой при сериализации.Вышеупомянутое решение для
JsonCreationConverter<T>
всего Интернета, но имеет недостаток, который проявляется в редких случаях. Новый JsonReader, созданный в методе ReadJson, не наследует ни одно из исходных значений конфигурации считывателя (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling и т. Д.). Эти значения должны быть скопированы перед использованием нового JsonReader в serializer.Populate ().Это лучшее, что я могу придумать, чтобы исправить некоторые проблемы с вышеупомянутой реализацией, но я все еще думаю, что некоторые вещи упускаются из виду
Обновление Я обновил это, чтобы иметь более явный метод, который делает копию существующего читателя. Это просто инкапсулирует процесс копирования отдельных настроек JsonReader. В идеале эта функция должна поддерживаться в самой библиотеке Newtonsoft, но сейчас вы можете использовать следующее:
Это следует использовать следующим образом:
Более старое решение следующее:
источник
Просто подумал, что поделюсь решением, также основанным на этом, которое работает с атрибутом Knowntype с использованием отражения, пришлось получить производный класс из любого базового класса, решение может извлечь выгоду из рекурсии, чтобы найти лучший соответствующий класс, хотя мне это не нужно в моем В этом случае сопоставление выполняется по типу, заданному для преобразователя, если у него есть KnownTypes, он будет сканировать их все, пока не будет сопоставлен тип, имеющий все свойства внутри строки json, и будет выбран первый для сопоставления.
использование так же просто, как:
в приведенном выше случае ret будет типа B.
JSON классы:
Код конвертера:
источник
Проект JsonSubTypes реализует универсальный конвертер, который обрабатывает эту функцию с помощью атрибутов.
Для конкретного примера, представленного здесь, как это работает:
источник
Это расширение к ответу тотема. В основном это то же самое, но сопоставление свойств основано на сериализованном объекте json, а не на объекте .net. Это важно, если вы используете [JsonProperty], используете CamelCasePropertyNamesContractResolver или делаете что-либо еще, что заставит json не соответствовать объекту .net.
Использование простое:
Код конвертера:
источник
В качестве другого варианта решения известного типа Totem вы можете использовать рефлексию для создания распознавателя универсального типа, чтобы избежать необходимости использовать атрибуты известных типов.
При этом используется метод, похожий на GenericResolver Джувала Лоуи для WCF.
Пока ваш базовый класс является абстрактным или интерфейсным, известные типы будут определяться автоматически, а не должны быть украшены известными атрибутами типов.
В моем собственном случае я решил использовать свойство $ type для обозначения типа в моем объекте json, а не пытаться определить его по свойствам, хотя вы могли бы позаимствовать здесь другие решения для использования определения на основе свойств.
Затем он может быть установлен как форматтер
источник
Вот еще одно решение, которое избегает использования
jObject.CreateReader()
и вместо этого создает новоеJsonTextReader
(такое поведение используется методом по умолчаниюJsonCreate.Deserialze
:источник
Часто реализация будет существовать в том же пространстве имен, что и интерфейс. Итак, я придумал это:
Следовательно, вы можете включить это глобально так:
источник
Используя идею totem и zlangner , я создал объект
KnownTypeConverter
, который сможет определить наиболее подходящего наследника, учитывая, что данные json могут не иметь дополнительных элементов.Итак, сервис отправляет JSON-ответ, содержащий массив документов (входящих и исходящих). Документы имеют как общий набор элементов, так и разные. В этом случае элементы, относящиеся к исходящим документам, являются необязательными и могут отсутствовать.
В связи с этим
Document
был создан базовый класс, который включает в себя общий набор свойств. Также создаются два класса-наследника: -OutgoingDocument
добавляет два необязательных элемента"device_id"
и"msg_id"
; -IncomingDocument
добавляет один обязательный элемент"sender_id"
;Задача состояла в том, чтобы создать конвертер, который на основе данных json и информации из KnownTypeAttribute сможет определить наиболее подходящий класс, который позволяет сохранять наибольшее количество получаемой информации. Следует также принять во внимание, что данные JSON могут не иметь дополнительных элементов. Чтобы уменьшить количество сравнений элементов json и свойств моделей данных, я решил не принимать во внимание свойства базового класса и сопоставлять с элементами json только свойства классов-наследников.
Данные из сервиса:
Модели данных:
Преобразователь:
PS: В моем случае, если ни один наследник не был выбран конвертером (это может произойти, если данные JSON содержат информацию только из базового класса или данные JSON не содержат необязательные элементы из
OutgoingDocument
), тогда объектOutgoingDocument
класса будет создан, так как он указан первым в спискеKnownTypeAttribute
атрибутов. По вашему желанию вы можете варьировать реализациюKnownTypeConverter
в данной ситуации.источник