Json.net сериализовать / десериализовать производные типы?

99

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, как десериализовать производные элементы.
Will
Я тебе это решил. У меня такая же проблема
Луис Карлос Чаваррия

Ответы:

46

Если вы храните тип в своем text(как и должно быть в этом сценарии), вы можете использовать расширение JsonSerializerSettings.

Смотрите: как десериализовать JSON в IEnumerable <BaseType> с помощью Newtonsoft JSON.NET

Но будьте осторожны. Использование чего-либо, кроме того, TypeNameHandling = TypeNameHandling.Noneможет привести к уязвимости системы безопасности .

Камраникус
источник
24
Вы также можете использовать TypeNameHandling = TypeNameHandling.Auto- это добавит $typeсвойство ТОЛЬКО для экземпляров, где объявленный тип (т.е. Base) не соответствует типу экземпляра (т.е. Derived). Таким образом, ваш JSON не раздувается так сильно, как TypeNameHandling.All.
AJ Richardson
Я продолжаю получать тип разрешения ошибок, указанный в JSON '..., ...'. Путь '$ type', строка 1, позиция 82. Есть идеи?
взятка
3
Будьте осторожны при использовании этого на общедоступной конечной точке, поскольку это создает проблемы с безопасностью: alphabot.com/security/blog/2017/net/…
gjvdkamp
1
@gjvdkamp JEEZ спасибо за это, я не знал об этом. Добавлю в свой пост.
kamranicus 02
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);

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

Madmenyo
источник
31
+1. Я искал в Google 30 минут, пока не обнаружил, что вам нужно использовать те же настройки для SerializeObject и DeserializeObject. Я предположил, что он будет использовать $ type неявно, если он присутствует при десериализации, глупо меня.
Erti-Chris Eelmaa
24
TypeNameHandling.Autoбудет делать это тоже, и это лучше, потому что он не записывает имя типа экземпляра, когда он соответствует типу поля / свойства, что часто имеет место для большинства полей / свойств.
Роман Старков
2
Это не работает, когда десериализация выполняется в другом решении / проекте. При сериализации имя Решения внедряется в виде типа: «SOLUTIONNAME.Models.Model». При десериализации в другом решении будет сгенерировано исключение «JsonSerializationException: не удалось загрузить сборку« SOLUTIONNAME ».
грустный разработчик CRUD,
19

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

Долгий путь - написать custom 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; }
}
Rzippo
источник
4
У меня есть потребность, но я не сторонник того, чтобы базовый класс знал обо всех "KnownSubType" ...
Мэтт Ноулз
2
Есть и другие варианты, если вы посмотрите документацию. Я привел только тот пример, который мне больше нравится.
rzippo 05
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"}
]
Дмитрий
источник