Używanie niezmiennych typów i właściwości

Niezmienny typ jest taki, który uniemożliwia zmianę wszelkich wartości właściwości lub pól obiektu po jego utworzeniu. Typ może być rekordem, nie mają właściwości publicznych ani pól, mają właściwości tylko do odczytu lub mają właściwości z zestawami prywatnymi lub tylko do inicjowania. System.String jest przykładem niezmiennego typu. System.Text.Json Udostępnia różne sposoby deserializacji danych JSON na niezmienne typy.

Konstruktory sparametryzowane

Domyślnie System.Text.Json używa domyślnego publicznego konstruktora bez parametrów. Można jednak powiedzieć, że używa konstruktora sparametryzowanego, co umożliwia deserializowanie niezmiennej klasy lub struktury.

  • W przypadku klasy, jeśli jedynym konstruktorem jest sparametryzowany, ten konstruktor będzie używany.

  • W przypadku struktury lub klasy z wieloma konstruktorami określ ten, który ma być używany, stosując atrybut [JsonConstructor]. Jeśli atrybut nie jest używany, publiczny konstruktor bez parametrów jest zawsze używany, jeśli istnieje.

    W poniższym przykładzie użyto atrybutu [JsonConstructor] :

    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()
            {
                string 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);
    
                Forecast forecast = JsonSerializer.Deserialize<Forecast>(json, options);
    
                Console.WriteLine($"forecast.Date: {forecast.Date}");
                Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}");
                Console.WriteLine($"forecast.Summary: {forecast.Summary}");
    
                string 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"}
    

    W programie .NET 7 i starszych wersjach [JsonConstructor] atrybut może być używany tylko z konstruktorami publicznymi.

Nazwy parametrów konstruktora sparametryzowanego muszą być zgodne z nazwami i typami właściwości. Dopasowanie jest bez uwzględniania wielkości liter, a parametr konstruktora musi być zgodny z rzeczywistą nazwą właściwości, nawet jeśli używasz właściwości [JsonPropertyName], aby zmienić nazwę właściwości. W poniższym przykładzie nazwa TemperatureC właściwości została zmieniona na celsius w formacie JSON, ale parametr konstruktora nadal nosi nazwę temperatureC:

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

namespace ImmutableTypesCtorParms
{
    public readonly 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()
        {
            string 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);

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

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

            string 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"}

Oprócz [JsonPropertyName]programu następujące atrybuty obsługują deserializacji z konstruktorami sparametryzowanymi:

Rekordy

Rekordy są również obsługiwane zarówno w przypadku serializacji, jak i deserializacji, jak pokazano w poniższym przykładzie:

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! }

Do nazw właściwości można zastosować dowolne atrybuty, używając property: elementu docelowego w atrybucie . Aby uzyskać więcej informacji na temat rekordów pozycyjnych, zobacz artykuł dotyczący rekordów w dokumentacji języka C#.

Niepublijne elementy członkowskie i metody dostępu do właściwości

Możesz włączyć użycie metody dostępu innej niż publiczna we właściwości przy użyciu atrybutu [JsonInclude], jak pokazano w poniższym przykładzie:

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"}

Dołączając właściwość z prywatnym zestawem, można nadal deserializacji tej właściwości.

W programie .NET 8 i nowszych wersjach można również użyć atrybutu [JsonInclude], aby wybrać elementy członkowskie inne niż publiczne do kontraktu serializacji dla danego typu.

Uwaga

W trybie generowania źródła nie można serializować elementów członkowskich ani używać private metod dostępu przez dodawanie adnotacji private do atrybutu [JsonInclude]. Ponadto można serializować internal elementy członkowskie lub używać internal metod dostępu, jeśli są one w tym samym zestawie co wygenerowany JsonSerializerContextelement .

Właściwości tylko do odczytu

W programie .NET 8 lub nowszych wersjach właściwości tylko do odczytu lub tych, które nie mają ustawienia prywatnego lub publicznego, mogą być również deserializowane. Chociaż nie można zmienić wystąpienia, do którego odwołuje się właściwość, jeśli typ właściwości jest modyfikowalny, można go zmodyfikować. Można na przykład dodać element do listy. Aby wykonać deserializowanie właściwości tylko do odczytu, należy ustawić zachowanie obsługi tworzenia obiektu, aby wypełnić zamiast zastąpić. Można na przykład dodać adnotację do właściwości za pomocą atrybutu JsonObjectCreationHandlingAttribute .

class A
{
    [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
    public List<int> Numbers1 { get; } = new List<int>() { 1, 2, 3 };
}

Aby uzyskać więcej informacji, zobacz Wypełnianie zainicjowanych właściwości.

Zobacz też