Исключить свойство enum модели из использования JsonStringEnumConverter, который глобально установлен при запуске? - PullRequest
1 голос
/ 20 января 2020

Я разрабатываю ASP. NET Core Application, используя самые последние версии. NET Core 3.1.1 и System.Text.Json, которые ранее использовали Newtonsoft.Json. В соответствии с рекомендациями Microsoft Руководство по миграции я внес изменения. Кроме того, так как большинство моих перечислений должны быть сериализованы как String, я настроил свой Startup.cs ConfigureServices для использования JsonStringEnumConverter глобально.

public void ConfigureServices(IServiceCollection services)
{
    // lines omitted for brevity

    services.AddControllers()
                .AddJsonOptions(options =>
                    {
                        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
                        options.JsonSerializerOptions.IgnoreNullValues = true;
                        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
                    });
}

Но недавно, после выпуска, мы поняли, что только несколько перечислений выдаются в виде цифр в json через наш API. Поскольку эти API используются внешне, замена цифр на строки может быть дорогостоящим занятием.

Итак, есть ли способ игнорировать универсальное для некоторых свойств перечисления, таких как украшение с атрибутом [JsonIgnore]?

1 Ответ

1 голос
/ 21 января 2020

JsonStringEnumConverter на самом деле является подклассом JsonConverterFactory. Он производит специфицированный c JsonConverterEnum для каждого конкретного типа enum, обнаруженного во время сериализации, который, в свою очередь, сериализует этот указанный c enum тип в виде строки.

Если вы не хотите, чтобы какой-либо указанный c enum тип был сериализован в виде строки, вы можете использовать шаблон декоратора и создать свою собственную фабрику преобразователей, которая декорирует JsonStringEnumConverter но предотвращает преобразование этого типа enum следующим образом:

public class OptOutJsonConverterFactory : JsonConverterFactoryDecorator
{
    readonly HashSet<Type> optOutTypes;

    public OptOutJsonConverterFactory(JsonConverterFactory innerFactory, params Type [] optOutTypes) : base(innerFactory) => this.optOutTypes = optOutTypes.ToHashSet();

    public override bool CanConvert(Type typeToConvert) => base.CanConvert(typeToConvert) && !optOutTypes.Contains(typeToConvert);
}

public class JsonConverterFactoryDecorator : JsonConverterFactory
{
    readonly JsonConverterFactory innerFactory;

    public JsonConverterFactoryDecorator(JsonConverterFactory innerFactory)
    {
        if (innerFactory == null)
            throw new ArgumentNullException(nameof(innerFactory));
        this.innerFactory = innerFactory;
    }

    public override bool CanConvert(Type typeToConvert) => innerFactory.CanConvert(typeToConvert);

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => innerFactory.CreateConverter(typeToConvert, options);
}

Затем используйте его в следующих опциях:

options.Converters.Add(new OptOutJsonConverterFactory(new JsonStringEnumConverter(), 
                                                      // Add here all enum types to serialize as integers:
                                                      typeof(SomeEnumNotToSerializeAsAString)
                                                      //, ...
                                                     ));

Примечания:

  • Если ведение списка типов перечислений для сериализации как целых неудобно, вы можете пометить перечисляемые типы как целые числа как некоторый пользовательский атрибут , а затем исключить типы, отмеченные этим атрибутом, из в пределах CanConvert(CanConvert(Type typeToConvert).

  • Шаблон декоратора необходим, потому что JsonStringEnumConverter запечатан.

Скрипка макета # 1 здесь .

В качестве альтернативы, если вы не хотите, чтобы некоторые Если для параметра c enum свойство будет сериализовано в виде строки, к свойству можно применить преобразователь, используя JsonConverterAttribute, который игнорирует входящий JsonSerializerOptions и генерирует значение по умолчанию. вместо сериализации:

/// <summary>
/// Apply this converter to a property to force the property to be serialized with default options.  
/// This converter can ONLY be applied to a property; setting it in options or on a type may cause a stack overflow exception!
/// </summary>
/// <typeparam name="T">the property's declared return type</typeparam>
public class SerializePropertyAsDefaultConverter<T> : JsonConverter<T>
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return JsonSerializer.Deserialize<T>(ref reader); // Ignore the incoming options!
    }

    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value); // Ignore the incoming options!
    } 
}

И примените его к своей модели следующим образом:

public class Model
{
    public StringEnum StringEnum { get; set; }

    [JsonConverter(typeof(SerializePropertyAsDefaultConverter<SomeEnumNotToSerializeAsAString>))]
    public SomeEnumNotToSerializeAsAString SomeEnumNotToSerializeAsAString { get; set; }
}

Примечания:

Скрипка макета # 2 здесь .

...