У меня есть следующий класс.
public class Foo
{
[XmlElement("Bar", typeof(Bar))]
[XmlElement("Pub", typeof(Pub))]
public BaseBar Bar { get; set; }
}
Я хотел бы перейти от сериализации XML к сериализации JSON (System.Text.Json), что эквивалентно атрибуту [XmlElement("Bar", typeof(Bar))]?
Смотрите этот образец: https://dotnetfiddle.net/pU8QAU
Редактировать:
Я ищу способ определить его на уровне свойств, поэтому, если у меня есть 2 свойства, я хотел бы иметь для них разные имена.
public class Foo
{
[XmlElement("Bar", typeof(Bar))]
[XmlElement("Pub", typeof(Pub))]
public BaseBar Bar { get; set; }
[XmlElement("Bar2", typeof(Bar))]
[XmlElement("Pub2", typeof(Pub))]
public BaseBar Bar2 { get; set; }
}
Смотрите этот образец: https://dotnetfiddle.net/M6nla
Редактировать2:
Данный ответ Гуру Строна дает этот результат
{"Bar":{"$type":"Pub" …
Я ищу
{"Pub":{…
Где Pub должно быть сериализовано из свойства Bar, если оно имеет тип Pub.
.NET 7 и последняя версия System.Text.Json представили поддержку полиморфной сериализации json. Один из способов справиться с этим — использовать JsonDerivedTypeAttribute для базового типа, указав всех потомков:
[JsonDerivedType(typeof(Bar), typeDiscriminator: nameof(Bar))]
[JsonDerivedType(typeof(Pub), typeDiscriminator: nameof(Pub))]
public abstract class BaseBar
{
public abstract string Text { get; set; }
}
public class Bar : BaseBar
{
public override string Text { get; set; } = "I am a Bar";
}
public class Pub : BaseBar
{
public override string Text { get; set; } = "I am a Pub";
}
Пользовательский преобразователь предыдущей версии .NET 7 можно было использовать для поддержки полиморфной десериализации.
Вы могли заставить полиморфную десериализацию работать до .NET 7, но это было не для слабонервных.
Поэтому я не могу указать это на уровне свойства, например. если бы у меня было два свойства с одним и тем же классом и оба с разными именами?
В качестве примера вы можете увидеть это: dotnetfiddle.net/M6nlaV
Еще раз посмотрел на вывод json вашего примера и возник еще один вопрос. Я бы предпочел, чтобы само имя свойства изменилось, чтобы отразить тип, поэтому вместо {"Bar":{"$type":"Pub" … я бы предпочел {"Pub":{…
@RandRandom это не то, как работает стандартная сериализация json, но вы можете написать свой собственный сериализатор. Это будет работать до тех пор, пока у вас не будет двух свойств одного и того же базового типа с одним и тем же фактическим типом значения (рекомендуется, чтобы ваши ключи в json были уникальными).
Чтобы сериализовать ваши данные, вам даже не нужен атрибут имени элемента, если у вас такое же имя свойства и вам не нужен корневой класс, вы можете использовать словарь
var foo = new Dictionary<string, BaseBar> {
{ "Bar", new Bar() },
{ "Pub", new Pub() }
};
var json = System.Text.Json.JsonSerializer.Serialize(foo,
new JsonSerializerOptions{WriteIndented = true}
);
Выход
{
"Bar": {
"Text": "I am a Bar"
},
"Pub": {
"Text": "I am a Pub"
}
}
Десериализовать
Dictionary<string, BaseBar> foo = JsonObject.Parse(json).AsObject()
.ToDictionary(p => p.Key, p => p.Key == "Bar" ?
(BaseBar)p.Value.Deserialize<Bar>()
: (BaseBar)p.Value.Deserialize<Pub>()
);
Я не думаю, что есть аналогичный атрибут, но вы можете выбрать пользовательский сериализатор или у вас могут быть оба свойства и третье, которое будет базовым типом и вернет первое ненулевое значение из них (и установщик вы установили одно из них на основе тип)