¿JsonStringEnumConverter (System.Text.Json) admite valores nulos?

8

Estoy cambiando mi código de .NET Core 2.x a .NET Core 3.x (es decir, uso la biblioteca nativa System.Text.Json). Al hacer esto, me encontré con algunos problemas con la forma en que el Newtonsoft.Jsonsoporte anterior para enumeraciones anulables no tiene una ruta de migración clara en este momento, ¿parece que no es compatible con .NET Core 3.x ?.

Por ejemplo, al usar Newtonsoft.Json, el convertidor JSON admite enumeraciones anulables, así:

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
}

La versión actual de la biblioteca nativa System.Text.Jsonno parece admitir esto.

¿Cómo resuelvo este problema? ¡No puedo migrar mi código!

Svek
fuente
1
El soporte nativo para el soporte de enumeración anulable en JsonStringEnumConverter se está rastreando en github.com/dotnet/corefx/issues/41307 .
Nitin Agarwal
@NitinAgarwal ¡Esperemos que se implemente pronto!
Svek

Respuestas:

6

Desafortunadamente, actualmente no hay soporte " System.Text.Jsonlisto para usar " para convertir enumeraciones anulables.

Sin embargo, existe una solución mediante el uso de su propio convertidor personalizado . (ver más abajo) .


La solución. Utiliza un convertidor personalizado.

Adjuntaría puede adjuntarlo a su propiedad al decorarlo con el convertidor personalizado:

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

Aquí está el convertidor:

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());
    }
}

¡Espero que ayude hasta que haya soporte nativo sin la necesidad de un convertidor personalizado!

Svek
fuente
1

Debería poder recuperar su comportamiento original instalando Newtonsoft JSON nuget y colocando esto en su código, supongo que está migrando una aplicación ASP:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddNewtonsoftJson();
}
Michal Hosala
fuente
1
La idea es usar el "mejor" más nuevo (rendimiento y compatibilidad de Microsoft a largo plazo) System.Text.Jsonque vino con ASP.NET Core 3.x. --- La "migración" mencionada fue de 2.xa 3.x
Svek
@Svek Puedo relacionarme con eso, sin embargo, todas las nuevas funciones brillantes de Json Core tienen algunas lagunas, por lo que por un tiempo el equipo decidió utilizar este enfoque, que espero que también sea útil para otras personas, ya que responde a su pregunta en su forma original: "¿cómo resolver este problema?".
Michal Hosala