Compartir a través de


Respetar las anotaciones que aceptan valores NULL

A partir de .NET 9, JsonSerializer cuenta con una funcionalidad (limitada) para aplicar tipos de referencia que no aceptan valores NULL en la serialización y deserialización. Puede activar o desactivar esta funcionalidad con la flag JsonSerializerOptions.RespectNullableAnnotations.

Por ejemplo, el siguiente fragmento de código genera una excepción JsonException durante la serialización con un mensaje como el siguiente:

La propiedad o el campo "Name" en el tipo "Person" no permite obtener valores NULL. Aquí podría pensar en actualizar la nulabilidad de las anotaciones.

    public static void RunIt()
    {
#nullable enable
        JsonSerializerOptions options = new()
        {
            RespectNullableAnnotations = true
        };

        Person invalidValue = new(Name: null!);
        JsonSerializer.Serialize(invalidValue, options);
    }

    record Person(string Name);

Del mismo modo, RespectNullableAnnotations aplica la nulabilidad en la deserialización. El siguiente fragmento de código genera una excepción JsonException durante la serialización con un mensaje como el siguiente:

El parámetro del constructor 'Name' en el tipo 'Person' no permite valores NULL. Aquí podría pensar en actualizar la nulabilidad de las anotaciones.

    public static void RunIt()
    {
#nullable enable
        JsonSerializerOptions options = new()
        {
            RespectNullableAnnotations = true
        };

        string json = """{"Name":null}""";
        JsonSerializer.Deserialize<Person>(json, options);
    }

    record Person(string Name);

Sugerencia

Limitaciones

Debido a la forma en que se implementan los tipos de referencia que no aceptan valores NULL, esta característica viene acompañada de algunas limitaciones importantes. Estudie bien estas limitaciones antes de activar la característica. La raíz del problema es que la nulabilidad del tipo de referencia no tiene ninguna representación de primera clase en el lenguaje intermedio (IL). Por tanto, las expresiones MyPoco y MyPoco? son indistinguibles desde la perspectiva de la reflexión en tiempo de ejecución. Aunque el compilador intenta compensarlo mediante la emisión de metadatos de atributo (consulte ejemplo sharplab.io), estos metadatos están restringidos a anotaciones de miembros no genéricos que tienen como ámbito una definición de tipo determinada. Esta limitación es la razón por la que la flag solo valida las anotaciones de nulabilidad presentes en propiedades, campos y parámetros de constructor no genéricos. System.Text.Json no admite la aplicación de nulabilidad en:

  • Tipos de nivel superior o el tipo que se pasa al realizar la primera llamada a JsonSerializer.Deserialize() o JsonSerializer.Serialize().
  • Tipos de elementos de colección: por ejemplo, los tipos List<string> y List<string?> son indistinguibles.
  • Todas las propiedades, campos o parámetros de constructor que son genéricos.

Si quiere que se aplique la nulabilidad en estos casos, elabore su tipo para que sea una estructura (ya que no admiten valores NULL) o cree un convertidor personalizado que invalide la propiedad HandleNull en true.

Conmutador de característica

Puede activar el parámetro de configuración RespectNullableAnnotations de forma global mediante la opción de la característica System.Text.Json.Serialization.RespectNullableAnnotationsDefault. Agregue el siguiente elemento de MSBuild al archivo del proyecto (por ejemplo, archivo .csproj):

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Text.Json.Serialization.RespectNullableAnnotationsDefault" Value="true" />
</ItemGroup>

La API RespectNullableAnnotationsDefault se ha implementado como una flag voluntaria en .NET 9 para evitar interrumpir las aplicaciones existentes. Si va a escribir una nueva aplicación, le recomendamos que habilite esta flag en el código.

Relación entre parámetros que aceptan valores NULL y opcionales

RespectNullableAnnotations no amplía esta aplicación a valores JSON no especificados, ya que System.Text.Json trata las propiedades correspondientes y que no aceptan valores NULL como conceptos ortogonales. Por ejemplo, el siguiente fragmento de código no genera una excepción durante la deserialización:

public static void RunIt()
{
    JsonSerializerOptions options = new()
    {
        RespectNullableAnnotations = true
    };
    var result = JsonSerializer.Deserialize<MyPoco>("{}", options);
    Console.WriteLine(result.Name is null); // True.
}

class MyPoco
{
    public string Name { get; set; }
}

Esta acción se deriva del propio lenguaje C#, donde puede tener propiedades necesarias que aceptan valores NULL:

MyPoco poco = new() { Value = null }; // No compiler warnings.

class MyPoco
{
    public required string? Value { get; set; }
}

Además, también puede tener propiedades opcionales que no aceptan valores NULL:

class MyPoco
{
    public string Value { get; set; } = "default";
}

La misma ortogonalidad se aplica a los parámetros del constructor:

record MyPoco(
    string RequiredNonNullable,
    string? RequiredNullable,
    string OptionalNonNullable = "default",
    string? OptionalNullable = "default"
    );

Consulte también