Sprache

Verwenden unveränderlicher Typen und Eigenschaften

Ein unveränderlicher Typ ist einer, der verhindert, dass Sie Eigenschaften- oder Feldwerte eines Objekts ändern, nachdem er instanziiert wurde. Der Typ kann ein Datensatz sein, keine öffentlichen Eigenschaften oder Felder haben, schreibgeschützte Eigenschaften aufweisen oder Eigenschaften mit privaten oder init-only-Settern haben. System.String ist ein Beispiel für einen unveränderlichen Typ. System.Text.Json bietet verschiedene Möglichkeiten, wie Sie JSON auf unveränderliche Typen deserialisieren können.

Parametrisierte Konstruktoren

Standardmäßig verwendet System.Text.Json den standardmäßigen öffentlichen parameterlosen Konstruktor. Sie können es jedoch anweisen, einen parametrisierten Konstruktor zu verwenden, der es ermöglicht, eine unveränderliche Klasse oder Struktur zu deserialisieren.

  • Wenn für eine Klasse der einzige Konstruktor ein parametrisierter Konstruktor ist, wird dieser Konstruktor verwendet.

  • Geben Sie für eine Struktur oder eine Klasse mit mehreren Konstruktoren die zu verwendende Klasse an, indem Sie das attribut [JsonConstructor] anwenden. Wenn das Attribut nicht verwendet wird, wird immer ein öffentlicher parameterloser Konstruktor verwendet, wenn vorhanden.

    Im folgenden Beispiel wird das attribut [JsonConstructor] verwendet:

    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 Run()
            {
                string json = """
                    {
                        "date":"2020-09-06T11:31:01.923395-07:00",
                        "temperatureC":-1,
                        "summary":"Cold"
                    }
                    """;
                Console.WriteLine($"Input JSON: {json}");
    
                var options = JsonSerializerOptions.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 forecast1 As Forecast = JsonSerializer.Deserialize(Of Forecast)(json, JsonSerializerOptions.Web)
    
                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, JsonSerializerOptions.Web)
    
                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"}
    

    In .NET 7 und früheren Versionen kann das attribut [JsonConstructor] nur mit öffentlichen Konstruktoren verwendet werden.

In .NET 8 und neueren Versionen unterstützt der Spiegelungsmodus nicht öffentliche Konstruktoren, die mit [JsonConstructor]gekennzeichnet sind. Ab .NET 11 unterstützt auch der Quellgenerierungsmodus diese.

Die Parameternamen eines parametrisierten Konstruktors müssen mit den Eigenschaftennamen und -typen übereinstimmen. Beim Abgleich wird die Groß-/Kleinschreibung ignoriert, und der Konstruktorparameter muss mit dem tatsächlichen Eigenschaftsnamen übereinstimmen, auch wenn Sie [JsonPropertyName] zum Umbenennen einer Eigenschaft verwenden. Im folgenden Beispiel wird der Name für die eigenschaft TemperatureC im JSON in celsius geändert, der Konstruktorparameter wird jedoch weiterhin temperatureCbenannt:

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 Run()
        {
            string json = """
                {
                    "date":"2020-09-06T11:31:01.923395-07:00",
                    "celsius":-1,
                    "summary":"Cold"
                }
                """;
            Console.WriteLine($"Input JSON: {json}");

            var options = JsonSerializerOptions.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"}

Neben [JsonPropertyName]unterstützen die folgenden Attribute die Deserialisierung mit parametrisierten Konstruktoren:

Parameter des By-Reference-Konstruktors

Ab .NET 11 deserialisiert JsonSerializer Typen, deren Konstruktorparameter die Modifikatoren in, ref, out und ref readonly verwenden.

Parametermodifikator Deserialisierungsverhalten
in, ref und ref readonly Der Serialisierer bindet jeden Parameter anhand des Namens und verwendet den zugrunde liegenden Elementtyp für den Typabgleich.
out Der Serialisierer bindet den Parameter nicht an JSON. Er verwirft den Wert, den der Konstruktor zuweist.

Im folgenden Konstruktor bindet der Serializer temperatureC an JSON. Es wird nicht gebunden isValid:

public Forecast(in int temperatureC, out bool isValid)
{
    TemperatureC = temperatureC;
    isValid = true;
}

In Visual Basic folgt ein ByRef Konstruktorparameter dem ref in der Tabelle gezeigten Verhalten:

Public Sub New(ByRef temperatureC As Integer)
    TemperatureC = temperatureC
End Sub

Aufzeichnungen

Datensätze werden auch für Serialisierung und Deserialisierung unterstützt, wie im folgenden Beispiel gezeigt:

using System.Text.Json;

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

    public class Program
    {
        public static void Run()
        {
            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! }

Sie können jedes der Attribute auf die Eigenschaftsnamen anwenden, indem Sie das property:-Ziel des Attributs verwenden. Weitere Informationen zu Positionsdatensätzen finden Sie im Artikel zu Datensätzen in der C#-Sprachreferenz.

Nicht öffentliche Member und Eigenschaftszugriffsmethoden

Sie können die Verwendung eines nicht öffentlichen Accessor für eine Eigenschaft aktivieren, indem Sie das Attribut [JsonInclude] verwenden, wie im folgenden Beispiel gezeigt:

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 Run()
        {
            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"}

Indem Sie eine Eigenschaft mit einem privaten Setter einschließen, können Sie diese Eigenschaft trotzdem deserialisieren.

In .NET 8 und höheren Versionen können Sie auch das [JsonInclude] Attribut verwenden, um nicht öffentliche Mitglieder in den Serialisierungsvertrag für einen bestimmten Typ zu aktivieren.

Ab .NET 11 unterstützt die Quellgenerierung private, internal und protected-Member, die Sie mit [JsonInclude] kennzeichnen. Es unterstützt auch private, internal und protected Accessoren für Eigenschaften, die Sie mit [JsonInclude] kennzeichnen. Die Quellgenerierung unterstützt auch nicht zugängliche Konstruktoren, die mit [JsonConstructor] gekennzeichnet sind.

Anmerkung

In .NET 10 und früheren Versionen unterstützt die Quellgenerierung keine private-Member oder protected-Accessoren. Durch das Anwenden des [JsonInclude] -Attributs auf das Element oder die Eigenschaft wird diese Einschränkung nicht entfernt. Die Quellgenerierung unterstützt internal Member und Accessoren nur, wenn sie sich in derselben Assembly wie das generierte JsonSerializerContext befinden. Sie unterstützt keine nicht zugänglichen Konstruktoren, auch wenn Sie sie mit [JsonConstructor] markieren.

Init-only-Eigenschaften

System.Text.Json deserialisiert reine init Eigenschaften wie jede andere festgelegte Eigenschaft. Ab .NET 11 wird ein vom Quell generierter Setter nur ausgeführt, wenn die JSON-Nutzlast die Eigenschaft enthält. Eine ausgelassene Eigenschaft behält ihren Initialisierungswert bei.

Schreibgeschützte Eigenschaften

In .NET 8 und höheren Versionen können schreibgeschützte Eigenschaften oder solche, die keinen Setter haben, entweder privat oder öffentlich, ebenfalls deserialisiert werden. Sie können die Instanz, auf die die Eigenschaft verweist, nicht ändern, aber wenn der Typ der Eigenschaft veränderlich ist, können Sie sie modifizieren. Sie können z. B. einer Liste ein Element hinzufügen. Um eine schreibgeschützte Eigenschaft zu deserialisieren, müssen Sie das Verhalten bei der Objekterstellung auf auffüllen statt ersetzen setzen. Beispielsweise können Sie die Eigenschaft mit dem attribut JsonObjectCreationHandlingAttribute kommentieren.

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

Weitere Informationen finden Sie unter Auffüllen initialisierter Eigenschaften.

Siehe auch