Como usar tipos imutáveis e acessadores não públicos com System.Text.JsonHow to use immutable types and non-public accessors with System.Text.Json

Este artigo mostra como usar tipos imutáveis, construtores com parâmetros públicos e acessadores não públicos com o System.Text.Json namespace.This article shows how to use immutable types, public parameterized constructors, and non-public accessors with the System.Text.Json namespace.

Tipos e registros imutáveisImmutable types and Records

System.Text.Json pode usar um construtor com parâmetros públicos, o que torna possível desserializar uma classe ou estrutura imutável.System.Text.Json can use a public parameterized constructor, which makes it possible to deserialize an immutable class or struct. Para uma classe, se o único Construtor for um parametrizado, esse construtor será usado.For a class, if the only constructor is a parameterized one, that constructor will be used. Para uma struct ou uma classe com vários construtores, especifique o que deve ser usado aplicando o atributo [JsonConstructor] .For a struct, or a class with multiple constructors, specify the one to use by applying the [JsonConstructor] attribute. Quando o atributo não é usado, um construtor público sem parâmetros é sempre usado, se estiver presente.When the attribute is not used, a public parameterless constructor is always used if present. O atributo só pode ser usado com construtores públicos.The attribute can only be used with public constructors. O exemplo a seguir usa o [JsonConstructor] atributo:The following example uses the [JsonConstructor] attribute:

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ImmutableTypes
{
    public struct Forecast
    {
        public DateTime Date { get; }
        public int TemperatureC { get; }
        public string Summary { get; }
 
        [JsonConstructor]
        public Forecast(DateTime date, int temperatureC, string summary) =>
            (Date, TemperatureC, Summary) = (date, temperatureC, summary);
    }

    public class Program
    {
        public static void Main()
        {
            var json = @"{""date"":""2020-09-06T11:31:01.923395-07:00"",""temperatureC"":-1,""summary"":""Cold""} ";
            Console.WriteLine($"Input JSON: {json}");

            var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);

            var forecast = JsonSerializer.Deserialize<Forecast>(json, options);

            Console.WriteLine($"forecast.Date: {forecast.Date}");
            Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}");
            Console.WriteLine($"forecast.Summary: {forecast.Summary}");

            var roundTrippedJson =
                JsonSerializer.Serialize<Forecast>(forecast, options);

            Console.WriteLine($"Output JSON: {roundTrippedJson}");

        }
    }
}

// Produces output like the following example:
//
//Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}
//forecast.Date: 9 / 6 / 2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}
Imports System.Text.Json
Imports System.Text.Json.Serialization

Namespace ImmutableTypes

    Public Structure Forecast
        Public ReadOnly Property [Date] As Date
        Public ReadOnly Property TemperatureC As Integer
        Public ReadOnly Property Summary As String

        <JsonConstructor>
        Public Sub New([Date] As Date, TemperatureC As Integer, Summary As String)
            Me.Date = [Date]
            Me.TemperatureC = TemperatureC
            Me.Summary = Summary
        End Sub

    End Structure

    Public NotInheritable Class Program

        Public Shared Sub Main()
            Dim json As String = "{""date"":""2020-09-06T11:31:01.923395-07:00"",""temperatureC"":-1,""summary"":""Cold""}"
            Console.WriteLine($"Input JSON: {json}")

            Dim options As New JsonSerializerOptions(JsonSerializerDefaults.Web)

            Dim forecast1 As Forecast = JsonSerializer.Deserialize(Of Forecast)(json, options)

            Console.WriteLine($"forecast.Date: {forecast1.[Date]}")
            Console.WriteLine($"forecast.TemperatureC: {forecast1.TemperatureC}")
            Console.WriteLine($"forecast.Summary: {forecast1.Summary}")

            Dim roundTrippedJson As String = JsonSerializer.Serialize(forecast1, options)

            Console.WriteLine($"Output JSON: {roundTrippedJson}")
        End Sub

    End Class

End Namespace

' Produces output like the following example:
'
'Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}
'forecast.Date: 9 / 6 / 2020 11:31:01 AM
'forecast.TemperatureC: -1
'forecast.Summary: Cold
'Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}

Os nomes de parâmetro de um construtor com parâmetros devem corresponder aos nomes de propriedade.The parameter names of a parameterized constructor must match the property names. A correspondência não diferencia maiúsculas de minúsculas e o parâmetro do construtor deve corresponder ao nome real da propriedade mesmo se você usar [JsonPropertyName] para renomear uma propriedade.Matching is case-insensitive, and the constructor parameter must match the actual property name even if you use [JsonPropertyName] to rename a property. No exemplo a seguir, o nome da TemperatureC propriedade é alterado para celsius no JSON, mas o parâmetro do Construtor ainda é nomeado temperatureC :In the following example, the name for the TemperatureC property is changed to celsius in the JSON, but the constructor parameter is still named temperatureC:

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ImmutableTypesCtorParms
{
    public struct Forecast
    {
        public DateTime Date { get; }
        [JsonPropertyName("celsius")]
        public int TemperatureC { get; }
        public string Summary { get; }
 
        [JsonConstructor]
        public Forecast(DateTime date, int temperatureC, string summary) =>
            (Date, TemperatureC, Summary) = (date, temperatureC, summary);
    }

    public class Program
    {
        public static void Main()
        {
            var json = @"{""date"":""2020-09-06T11:31:01.923395-07:00"",""celsius"":-1,""summary"":""Cold""} ";
            Console.WriteLine($"Input JSON: {json}");

            var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);

            var forecast = JsonSerializer.Deserialize<Forecast>(json, options);

            Console.WriteLine($"forecast.Date: {forecast.Date}");
            Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}");
            Console.WriteLine($"forecast.Summary: {forecast.Summary}");

            var roundTrippedJson =
                JsonSerializer.Serialize<Forecast>(forecast, options);

            Console.WriteLine($"Output JSON: {roundTrippedJson}");

        }
    }
}

// Produces output like the following example:
//
//Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","celsius":-1,"summary":"Cold"}
//forecast.Date: 9 / 6 / 2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","celsius":-1,"summary":"Cold"}

Além [JsonPropertyName] dos atributos a seguir, há suporte para desserialização com construtores com parâmetros:Besides [JsonPropertyName] the following attributes support deserialization with parameterized constructors:

Também há suporte para registros no C# 9, conforme mostrado no exemplo a seguir:Records in C# 9 are also supported, as shown in the following example:

#nullable enable
using System;
using System.Text.Json;

namespace Records
{
    public record Forecast(DateTime Date, int TemperatureC)
    {
        public string? Summary { get; init; }
    };

    public class Program
    {
        public static void Main()
        {
            Forecast forecast = new(DateTime.Now, 40)
            {
                Summary = "Hot!"
            };

            string forecastJson = JsonSerializer.Serialize<Forecast>(forecast);
            Console.WriteLine(forecastJson);
            Forecast? forecastObj = JsonSerializer.Deserialize<Forecast>(forecastJson);
            Console.WriteLine(forecastObj);
        }
    }
}

// Produces output like the following example:
//
//{ "Date":"2020-10-21T15:26:10.5044594-07:00","TemperatureC":40,"Summary":"Hot!"}
//Forecast { Date = 10 / 21 / 2020 3:26:10 PM, TemperatureC = 40, Summary = Hot! }

Para tipos que são imutáveis porque todos os seus setters de propriedade são não públicos, consulte a seção a seguir sobre acessadores de propriedade não pública.For types that are immutable because all their property setters are non-public, see the following section about non-public property accessors.

JsonConstructorAttribute e o suporte a registros do C# 9 não está disponível no .NET Core 3,1.JsonConstructorAttribute and C# 9 Record support aren't available in .NET Core 3.1.

Acessadores de propriedade não públicaNon-public property accessors

Para habilitar o uso de um acessador de propriedade não público, use o atributo [JsonInclude] , conforme mostrado no exemplo a seguir:To enable use of a non-public property accessor, use the [JsonInclude] attribute, as shown in the following example:


using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace NonPublicAccessors
{
    public class Forecast
    {
        public DateTime Date { get; init; }

        [JsonInclude]
        public int TemperatureC { get; private set; }

        [JsonInclude]
        public string Summary { private get; set; }
    };

    public class Program
    {
        public static void Main()
        {
            string json = @"{""Date"":""2020-10-23T09:51:03.8702889-07:00"",""TemperatureC"":40,""Summary"":""Hot""}";
            Console.WriteLine($"Input JSON: {json}");

            Forecast forecastDeserialized = JsonSerializer.Deserialize<Forecast>(json);
            Console.WriteLine($"Date: {forecastDeserialized.Date}");
            Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}");

            json = JsonSerializer.Serialize<Forecast>(forecastDeserialized);
            Console.WriteLine($"Output JSON: {json}");
        }
    }
}

// Produces output like the following example:
//
//Input JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
//Date: 10 / 23 / 2020 9:51:03 AM
//TemperatureC: 40
//Output JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
Imports System.Text.Json
Imports System.Text.Json.Serialization

Namespace NonPublicAccessors

    Public Class Forecast
        Public Property [Date] As Date

        Private _temperatureC As Integer

        <JsonInclude>
        Public Property TemperatureC As Integer
            Get
                Return _temperatureC
            End Get
            Private Set(Value As Integer)
                _temperatureC = Value
            End Set
        End Property

        Private _summary As String

        <JsonInclude>
        Public Property Summary As String
            Private Get
                Return _summary
            End Get
            Set(Value As String)
                _summary = Value
            End Set
        End Property

    End Class

    Public NotInheritable Class Program

        Public Shared Sub Main()
            Dim json As String = "{""Date"":""2020-10-23T09:51:03.8702889-07:00"",""TemperatureC"":40,""Summary"":""Hot""}"
            Console.WriteLine($"Input JSON: {json}")

            Dim forecastDeserialized As Forecast = JsonSerializer.Deserialize(Of Forecast)(json)
            Console.WriteLine($"Date: {forecastDeserialized.[Date]}")
            Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}")

            json = JsonSerializer.Serialize(forecastDeserialized)
            Console.WriteLine($"Output JSON: {json}")
        End Sub

    End Class

End Namespace

' Produces output like the following example:
'
'Input JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
'Date: 10 / 23 / 2020 9:51:03 AM
'TemperatureC: 40
'Output JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}

Não há suporte para acessadores de propriedade não pública no .NET Core 3,1.Non-public property accessors are not supported in .NET Core 3.1. Para obter mais informações, consulte o Newtonsoft.Json artigo migrar de.For more information, see the Migrate from Newtonsoft.Json article.

Veja tambémSee also