Json.net серіалізує / десеріалізує похідні типи?


98

json.net (newtonsoft)
Я переглядаю документацію, але не можу знайти нічого щодо цього чи найкращого способу це зробити.

public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

JsonConvert.Deserialize<List<Base>>(text);

Тепер я маю похідні об'єкти в серіалізованому списку. Як десеріалізувати список і повернути похідні типи?


Спадщина працює не так. Ви можете вказати JsonConvert.Deserialize <Derived> (текст); включити поле Ім'я. Оскільки Derived IS A Base (а не навпаки), Base не знає нічого про визначення Derived.
M.Babcock

Вибачте, трохи уточнив. Проблема в тому, що у мене є список, який містить як базові, так і похідні об’єкти. Тому мені потрібно зрозуміти, як я кажу newtonsoft, як десериалізувати похідні елементи.
Буде

Я це вирішив. У мене така сама проблема
Луїс Карлос Чаваррія

Відповіді:


46

Якщо ви зберігаєте тип у своєму text(як і слід було робити в цьому випадку), ви можете використовувати файл JsonSerializerSettings.

Дивіться: як десеріалізувати JSON в IEnumerable <BaseType> за допомогою Newtonsoft JSON.NET

Але будьте обережні. Використовуючи будь-що інше, ніж TypeNameHandling = TypeNameHandling.Noneможе відкрити себе для вразливості системи безпеки .


24
Ви також можете використовувати TypeNameHandling = TypeNameHandling.Auto- це додасть $typeвластивість ТІЛЬКИ для екземплярів, де оголошений тип (тобто Base) не відповідає типу екземпляра (тобто Derived). Таким чином, він не так сильно здуває ваш JSON TypeNameHandling.All.
Ей Джей Річардсон,

Я постійно отримую тип вирішення помилок, вказаний у JSON '..., ...'. Шлях '$ type', рядок 1, позиція 82. Будь-які ідеї?
briba

3
Будьте обережні, використовуючи це на загальнодоступній кінцевій точці, оскільки це відкриває проблеми безпеки: alphabot.com/security/blog/2017/net/…
gjvdkamp

1
@gjvdkamp JEEZ дякую за це, я не знав про це. Додасть до мого допису.
kamranicus

96

Ви повинні ввімкнути обробку імен типів і передати це (де) серіалізатору як параметр налаштувань.

Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };

JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);

Це призведе до правильної десеріалізації похідних класів. Недоліком цього є те, що він буде називати всі об’єкти, якими ви користуєтесь, як такий, він називатиме список, в який ви поміщаєте об’єкти.


31
+1. Я гуглив 30 хвилин, поки насправді не дізнався, що вам потрібно використовувати однакові налаштування для SerializeObject & DeserializeObject. Я припускав, що явно використовуватиме тип $, якщо він є там при десеріалізації, безглуздо.
Erti-Chris Eelmaa

24
TypeNameHandling.Autoзробить це теж, і це приємніше, оскільки він не пише ім'я типу екземпляра, коли воно відповідає типу поля / властивості, що часто буває для більшості полів / властивостей.
Роман Старков

2
Це не працює, коли десеріалізація виконується для іншого рішення / проекту. При серіалізації ім'я Рішення вбудовується у тип: "ІМЯ РОЗЧИНУ.Моделі.Модель". При десеріалізації іншого рішення він викине "JsonSerializationException: Не вдалося завантажити збірку 'SOLUTIONNAME'.
Сумний розробник CRUD

19

Оскільки питання настільки популярне, може бути корисно додати, що робити, якщо ви хочете контролювати ім'я властивості типу та його значення.

Довгий шлях - написати власні JsonConverters для обробки (де) серіалізації шляхом ручної перевірки та встановлення властивості type.

Більш простим способом є використання JsonSubTypes , який обробляє всі шаблони за допомогою атрибутів:

[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
    public virtual string Sound { get; }
    public string Color { get; set; }
}

public class Dog : Animal
{
    public override string Sound { get; } = "Bark";
    public string Breed { get; set; }
}

public class Cat : Animal
{
    public override string Sound { get; } = "Meow";
    public bool Declawed { get; set; }
}

3
У мене є потреба, але я не прихильник того, щоб базовий клас знав про всі "KnownSubType" ...
Метт Ноулз,

2
Є інші варіанти, якщо ви подивитесь на документацію. Я навів лише той приклад, який мені подобається більше.
rzippo

1
Це більш безпечний підхід, який не піддає вашій службі завантаження довільних типів при десериалізації.
Девід Бург

3

Використовуйте цей JsonKnownTypes , це дуже подібний спосіб використання, він просто додає дискримінатор до json:

[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))]
[JsonKnownType(typeof(Base), "base")]
[JsonKnownType(typeof(Derived), "derived")]
public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

Тепер, коли ви серіалізуєте об'єкт у json, буде додано "$type"з "base"і "derived"значення, і він буде використаний для десеріалізації

Приклад серіалізованого списку:

[
    {"Name":"some name", "$type":"base"},
    {"Name":"some name", "Something":"something", "$type":"derived"}
]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.