JsonStringEnumConverter (System.Text.Json) 是否支持空值?

Sve*_*vek 15 c# .net-core system.text.json

我正在将我的代码从 .NET Core 2.x 转移到 .NET Core 3.x(即使用本机库System.Text.Json)。在这样做时,我遇到了一些问题,因为以前Newtonsoft.Json对可空枚举的支持目前没有明确的迁移路径——看起来 .NET Core 3.x 不支持它?。

例如,使用Newtonsoft.Json,JSON 转换器支持可为空的枚举,如下所示:

public enum UserStatus
{
    NotConfirmed,
    Active,
    Deleted
}

public class User
{
    public string UserName { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]  // using Newtonsoft.Json
    public UserStatus? Status { get; set; }       // Nullable Enum
}
Run Code Online (Sandbox Code Playgroud)

本机库的当前版本System.Text.Json,似乎不支持这一点。

我该如何解决这个问题?我无法迁移我的代码!

Sve*_*vek 12

不幸的是,目前不支持“开箱即用”System.Text.Json来转换可为空的枚举。

但是,有一个解决方案是使用您自己的自定义转换器(见下文)


解决方案。使用自定义转换器。

您可以通过使用自定义转换器装饰它来将其附加到您的财产上:

// using System.Text.Json
[JsonConverter(typeof(StringNullableEnumConverter<UserStatus?>))]  // Note the '?'
public UserStatus? Status { get; set; }                            // Nullable Enum
Run Code Online (Sandbox Code Playgroud)

这是转换器:

public class StringNullableEnumConverter<T> : JsonConverter<T>
{
    private readonly JsonConverter<T> _converter;
    private readonly Type _underlyingType;

    public StringNullableEnumConverter() : this(null) { }

    public StringNullableEnumConverter(JsonSerializerOptions options)
    {
        // for performance, use the existing converter if available
        if (options != null)
        {
            _converter = (JsonConverter<T>)options.GetConverter(typeof(T));
        }

        // cache the underlying type
        _underlyingType = Nullable.GetUnderlyingType(typeof(T));
    }

    public override bool CanConvert(Type typeToConvert)
    {
        return typeof(T).IsAssignableFrom(typeToConvert);
    }

    public override T Read(ref Utf8JsonReader reader, 
        Type typeToConvert, JsonSerializerOptions options)
    {
        if (_converter != null)
        {
            return _converter.Read(ref reader, _underlyingType, options);
        }

        string value = reader.GetString();

        if (String.IsNullOrEmpty(value)) return default;

        // for performance, parse with ignoreCase:false first.
        if (!Enum.TryParse(_underlyingType, value, 
            ignoreCase: false, out object result) 
        && !Enum.TryParse(_underlyingType, value, 
            ignoreCase: true, out result))
        {
            throw new JsonException(
                $"Unable to convert \"{value}\" to Enum \"{_underlyingType}\".");
        }

        return (T)result;
    }

    public override void Write(Utf8JsonWriter writer, 
        T value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value?.ToString());
    }
}
Run Code Online (Sandbox Code Playgroud)

希望在不需要自定义转换器的情况下提供本机支持之前有所帮助!


Ssh*_*ack 9

对于 Dotnet 6,这是开箱即用的:

public enum UserStatus
{
    NotConfirmed,
    Active,
    Deleted
}

public class User
{
    public string UserName { get; set; }

    [JsonConverter(typeof(JsonStringEnumConverter))]
    public UserStatus? Status { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在 Dotnet fiddle 上尝试一下

查看官方文档