Я пытаюсь настроить считыватель, который будет принимать объекты JSON с различных веб-сайтов (подумайте о сборке информации) и переводит их в объекты C #. В настоящее время я использую JSON.NET для процесса десериализации. Проблема, с которой я сталкиваюсь, заключается в том, что он не знает, как обрабатывать свойства уровня интерфейса в классе. Итак, что-то от природы:
public IThingy Thing
Произойдет ошибка:
Не удалось создать экземпляр типа IThingy. Тип - это интерфейс или абстрактный класс, и его нельзя создать.
Относительно важно, чтобы он был IThingy, а не Thingy, поскольку код, над которым я работаю, считается чувствительным, а модульное тестирование очень важно. Мокинг объектов для скриптов атомарного тестирования невозможен с полноценными объектами, такими как Thingy. Они должны быть интерфейсом.
Я уже некоторое время изучаю документацию JSON.NET, и все вопросы, которые я мог найти на этом сайте, связанные с этим, возникли более года назад. Любая помощь?
Кроме того, если это важно, мое приложение написано на .NET 4.0.
Ответы:
@SamualDavis предоставил отличное решение в связанном вопросе , который я резюмирую здесь.
Если вам необходимо десериализовать поток JSON в конкретный класс, который имеет свойства интерфейса, вы можете включить конкретные классы в качестве параметров в конструктор класса! Десериализатор NewtonSoft достаточно умен, чтобы понять, что ему нужно использовать эти конкретные классы для десериализации свойств.
Вот пример:
источник
[JsonConstructor]
атрибута.(Скопировано из этого вопроса )
В случаях, когда у меня не было контроля над входящим JSON (и поэтому я не могу гарантировать, что он включает свойство $ type), я написал собственный преобразователь, который просто позволяет вам явно указать конкретный тип:
Здесь просто используется реализация сериализатора по умолчанию из Json.Net с явным указанием конкретного типа.
Обзор доступен в этом сообщении в блоге . Исходный код ниже:
источник
ConcreteListTypeConverter<TInterface, TImplementation>
для обработки членов класса типаIList<TInterface>
.concreteTypeConverter
Хотя было бы лучше иметь фактический код в вопросе.ConcreteListTypeConverter<TInterface, TImplementation>
реализацию?Зачем нужен конвертер? Для
Newtonsoft.Json
решения этой конкретной проблемы есть встроенная функция :Набор
TypeNameHandling
вJsonSerializerSettings
кTypeNameHandling.Auto
Это поместит каждый тип в json, который хранится не как конкретный экземпляр типа, а как интерфейс или абстрактный класс.
Убедитесь, что вы используете одни и те же настройки для сериализации и десериализации. .
Я протестировал его, и он отлично работает даже со списками.
Результаты поиска Веб-результат со ссылками на сайты
⚠️ ВНИМАНИЕ :
Используйте это только для json из известного и надежного источника. Пользователь snipsnipsnip правильно заметил, что это действительно уязвимость.
См. CA2328 и SCS0028 для получения дополнительной информации.
Источник и альтернативная ручная реализация: Блог Code Inside
источник
Чтобы включить десериализацию нескольких реализаций интерфейсов, вы можете использовать JsonConverter, но не через атрибут:
DTOJsonConverter сопоставляет каждый интерфейс с конкретной реализацией:
DTOJsonConverter требуется только для десериализатора. Процесс сериализации не изменился. В объект Json не нужно встраивать имена конкретных типов.
В этом сообщении SO предлагается то же решение на один шаг вперед с помощью универсального JsonConverter.
источник
FullName
s, когда вы можете просто сравнивать типы напрямую?Используйте этот класс для сопоставления абстрактного типа с реальным типом:
... и при десериализации:
источник
where TReal : TAbstract
чтобы убедиться, что он может быть приведен к типуwhere TReal : class, TAbstract, new()
.Николас Уэстби предоставил отличное решение в замечательной статье .
Если вы хотите десериализацию JSON в один из множества возможных классов, реализующих такой интерфейс:
Вы можете использовать собственный конвертер JSON:
И вам нужно будет украсить свойство "Profession" атрибутом JsonConverter, чтобы дать ему знать, что нужно использовать ваш собственный конвертер:
А затем вы можете преобразовать свой класс с помощью интерфейса:
источник
Вы можете попробовать две вещи:
Реализуйте модель пробного / синтаксического анализа:
Или, если вы можете сделать это в своей объектной модели, реализуйте конкретный базовый класс между IPerson и конечными объектами и десериализуйте его.
Первый потенциально может дать сбой во время выполнения, второй требует изменений в вашей объектной модели и гомогенизирует вывод до наименьшего общего знаменателя.
источник
Я нашел это полезным. Вы тоже могли бы.
Пример использования
Конвертер пользовательского творчества
Документация Json.NET
источник
Для тех, кому может быть интересно узнать о ConcreteListTypeConverter, на который ссылался Оливер, вот моя попытка:
источник
CanConvert(Type objectType) { return true;}
. Кажется хакерским, насколько это полезно? Возможно, я ошибаюсь, но разве это не то же самое, что сказать маленькому неопытному бойцу, что он выиграет бой независимо от соперника?Что бы это ни стоило, мне пришлось по большей части справляться с этим самому. У каждого объекта есть метод Deserialize (string jsonStream) . Несколько его фрагментов:
В этом случае new Thingy (string) - это конструктор, который будет вызывать метод Deserialize (string jsonStream) соответствующего конкретного типа. Эта схема будет продолжать идти вниз и вниз, пока вы не дойдете до базовых точек, которые json.NET может просто обработать.
Так далее и тому подобное. Эта установка позволила мне предоставить настройки json.NET, с которыми он может справиться, без необходимости рефакторинга большой части самой библиотеки или использования громоздких моделей try / parse, которые увязли бы всю нашу библиотеку из-за количества задействованных объектов. Это также означает, что я могу эффективно обрабатывать любые изменения json для определенного объекта, и мне не нужно беспокоиться обо всем, что касается объекта. Это ни в коем случае не идеальное решение, но оно неплохо работает по результатам нашего модульного и интеграционного тестирования.
источник
Предположим, что настройка autofac выглядит следующим образом:
Затем предположим, что ваш класс такой:
Следовательно, использование резольвера при десериализации может быть таким:
Вы можете увидеть более подробную информацию в http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
источник
Никакой объект никогда не будет Чужим, поскольку все интерфейсы абстрактны по определению.
Первоначально сериализованный у вас объект имел конкретный тип, реализующий абстрактный интерфейс. Вам нужен такой же бетон класс оживил сериализованные данные.
Полученный объект будет иметь какой - то тип , который реализует абстрактный интерфейс , которого вы ищете.
Из документации следует, что можно использовать
при десериализации сообщить JSON.NET о конкретном типе.
источник
_type
свойство, указывающее , какой конкретный тип использовать.Мое решение этого, которое мне нравится, потому что оно довольно общее, заключается в следующем:
}
Очевидно, вы могли бы тривиально преобразовать его в еще более общий преобразователь, добавив конструктор, принимающий аргумент типа Dictionary <Type, Type>, с помощью которого можно создать экземпляр переменной экземпляра преобразования.
источник
Спустя несколько лет у меня возникла похожая проблема. В моем случае были сильно вложенные интерфейсы и предпочтение для генерации конкретных классов во время выполнения, чтобы он работал с универсальным классом.
Я решил создать прокси-класс во время выполнения, который будет обертывать объект, возвращаемый Newtonsoft.
Преимущество этого подхода состоит в том, что он не требует конкретной реализации класса и может автоматически обрабатывать любую глубину вложенных интерфейсов. Вы можете узнать об этом больше в моем блоге .
Использование:
источник
PopulateObject
прокси-сервера, созданного Impromptu Interface. К сожалению, я отказался от Duck Typing - просто было проще создать собственный сериализатор контрактов Json, который использовал отражение, чтобы найти существующую реализацию запрошенного интерфейса и использовать ее.Используйте этот JsonKnownTypes , это очень похожий способ использования, он просто добавляет дискриминатор в json:
Теперь, когда вы сериализуете объект в json, он будет добавлен
"$type"
с помощью"myClass"
значением и будет использоваться для десериализацииJson:
источник
Моим решением были добавлены элементы интерфейса в конструктор.
источник