Как мне десериализовать json в мой класс, который наследуется от базового класса, но свойства в базовом классе не соответствуют json? Я не могу управлять базовым классом. Например:
{
"prop1": "Value1",
"prop2": "Value2"
}
Я хочу десериализовать выше в MyClass:
public class MyClass : Base
{
[JsonPropertyName("prop2")]
public string? Property2 { get; set; }
}
public class Base // I do not own this
{
public string? Property1 { get; init; }
}
Должен ли я переопределять базовые свойства и украшать их?
@Pawel, ваш код не может быть скомпилирован по событию. Пожалуйста, опубликуйте правильный
@Павел {получить; в этом; } "{get;init;}" как это инициализируется?
@Серж, я не понимаю, что ты имеешь в виду. Property1 доступен только для инициализации.
@Pawel Да, но где код для его инициализации? У него есть специальный конструктор?
Вам нужно сериализовать или только десериализовать?
@Serge Без явного конструктора я бы предположил, что свойства только для инициализации могут быть инициализированы только с помощью инициализаторов объектов. Например MyClass a = new() { Property1 = "v1" };
@dbc Только десериализация.
Если вы хотите использовать то же имя свойства, вам следует скрыть свойство базового класса с помощью ключевого слова new и пометить его атрибутом JsonPropertyName:
public class MyClass : Base
{
[JsonPropertyName("prop2")]
public string? MyProperty2 { get; set; }
[JsonPropertyName("prop1")]
public new string? Property1 { get; set; }
}
Также вы можете реализовать JsonConverter для типа вместо того, чтобы находить определенные свойства в JSON и сопоставлять их со свойствами объекта. Это защитит вашу модель от конкретной модели JSON.
public class MyClassJsonConverter : JsonConverter<MyClass>
{
public override MyClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var jsonDocument = JsonDocument.ParseValue(ref reader);
var rootElement = jsonDocument.RootElement;
var myClass = new MyClass();
myClass.Property1 = rootElement.GetProperty("prop1").GetString();
myClass.Property2 = rootElement.GetProperty("prop2").GetString();
return myClass;
}
public override void Write(Utf8JsonWriter writer, MyClass value, JsonSerializerOptions options)
{
// imlement logic here if needed
throw new NotImplementedException();
}
}
[JsonConverter(typeof(MyClassJsonConverter))]
public class MyClass : Base
{
public string? MyProperty2 { get; set; }
}
Также здесь есть большая подробная статья "Как написать собственные конвертеры для сериализации JSON (marshalling) в .NET" с примерами конвертеров, фабрикой конвертеров, регистрацией конвертеров обработки ошибок и другими аспектами конвертеров для сериализации JSON.
Например, вам не нужно использовать JsonConverterAttribute и вызывать Deserialize с явно указанными преобразователями:
public class MyClass : Base
{
public string? MyProperty2 { get; set; }
}
var serializeOptions = new JsonSerializerOptions
{
WriteIndented = true,
Converters =
{
new MyClassJsonConverter()
}
};
var myClass = JsonSerializer.Deserialize<MyClass>(jsonString, deserializeOptions)!;
"с новым ключевым словом и отметить атрибутом JsonPropertyName:" - но лучше этого не делать =)
@GuruStron Да, я согласен с тем, что это плохая практика, а также в целом модель должна быть независимой от формата хранения. Тогда есть лучшее и сложное решение с JsonConverter :)
Класс не может наследовать от записи
@Серж, я исправил свой вопрос. Спасибо за указание на это.
@vadim-martynov Спасибо, я обнаружил, что подход «скрыть с ключевым словом new» работает лучше всего в моем случае. Мне пришлось немного изменить реализацию, потому что базовый класс выполнял некоторую логику с Property1 при инициализации, и чтобы базовое значение не было нулевым, я сделал это: [JsonPropertyName("prop1")] public new string? Property1 { get => base.Property1; init => base.Property1 = value; }. Почему вы считаете это плохой практикой?
Вы можете использовать собственный JsonConverter от @VadimMartynov, но более простой способ — добавить свойство
public class MyClass : Base
{
[JsonPropertyName("prop2")]
public string? Property2 { get; set; }
[JsonPropertyName("prop1")]
public new string? Property1
{
get { return base.Property1; }
init { base.Property1 = value; }
}
}
Но я рекомендую вам начать использовать Newtonsoft.Json. Этот код был бы намного понятнее
public class MyClass : Base
{
[JsonProperty("prop2")]
public string? Property2 { get; set; }
[JsonConstructor]
public MyClass(string? prop1)
{
Property1 = prop1;
}
}
У вас есть контроль над базовым классом? Вы можете внести в это изменения?