Вопрос или проблема
Я хочу, чтобы System.Text.Json всегда добавлял TypeDiscriminator
Я использую FastEndpoints
для создания API, который использует System.Text.Json
для сериализации данных.
Моя модель данных выглядит примерно так:
[JsonDerivedType(typeof(Type1), typeDiscriminator: "type1")]
[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
abstract record MyBase(Guid Id);
record Type1(Guid Id, string MyProp) : MyBase(Id);
Когда я возвращаю List<MyBase>
, сериализатор добавляет дискриминатор типа в результирующий JSON:
[
{
"type": "type1",
"id": "1234567890",
"myProp": "fu"
}
]
Но когда я возвращаю один MyBase
, сериализатор пропускает дискриминатор типа:
{
"id": "1234567890",
"myProp": "fu"
}
Как сделать так, чтобы в этом случае также добавлялся дискриминатор типа?
ПРАВКА
Под возвратом
я имею в виду, когда вызывается SendOkAsync
😉
Ответ или решение
Чтобы заставить сериализатор System.Text.Json
всегда добавлять свойство TypeDiscriminator
, даже когда вы возвращаете один объект MyBase
, вам нужно настроить кастомный конвертер. Это позволит вам контролировать процесс сериализации и гарантировать, что discriminator будет добавляться независимо от того, возвращается ли список или одиночный элемент.
Вот шаги, которые вам нужно выполнить:
- Создайте пользовательский конвертер: Вам понадобится написать специальный конвертер для вашего базового типа, который будет добавлять свойство
type
при сериализации.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
public class MyBaseConverter : JsonConverter<MyBase>
{
public override MyBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// Логика чтения объекта из JSON.
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, MyBase value, JsonSerializerOptions options)
{
// Сначала сериализуем базовый объект
var type = value.GetType();
JsonSerializer.Serialize(writer, value, type, options);
// Затем добавляем discriminator
writer.WriteStartObject();
writer.WriteString("type", type == typeof(Type1) ? "type1" : "unknown");
writer.WriteEndObject();
}
}
- Зарегистрируйте конвертер: Не забудьте зарегистрировать ваш конвертер в настройках сериализации, чтобы он использовался при сериализации объектов
MyBase
.
var options = new JsonSerializerOptions
{
Converters = { new MyBaseConverter() },
WriteIndented = true
};
- Используйте настроенный
JsonSerializerOptions
: Когда вы будете вызыватьSendOkAsync
, убедитесь, что используете настройки, которые вы создали.
await SendOkAsync(result, options: options);
Теперь, при вызове SendOkAsync
с одиночным объектом MyBase
, сериализатор должен добавлять свойство type
, как и в случае с массивом.
Пример
Теперь, если вы будете возвращать объект типа Type1
, результат будет выглядеть следующим образом:
{
"type": "type1",
"id": "1234567890",
"myProp": "fu"
}
Заключение
Таким образом, добавление собственного конвертера для обработки MyBase
позволяет вам обеспечить согласованность в сериализации объектов, гарантируя, что свойства типа будут всегда добавлены в JSON, независимо от структуры вывода (список или один объект).