Language

変更できない型とプロパティを使用する

変更できない 型 は、インスタンス化された後にオブジェクトのプロパティまたはフィールド値を変更できないようにするものです。 この型は、レコードであったり、パブリック プロパティやフィールドを持たなかったり、読み取り専用プロパティを持っていたり、プライベートまたは初期化時のみのセッターを持つプロパティを持っていたりする可能性があります。 System.String は、変更できない型の例です。 System.Text.Json では、JSON を不変型に逆シリアル化するさまざまな方法が提供されます。

パラメーター化されたコンストラクター

既定では、System.Text.Json は既定のパブリック パラメーターなしのコンストラクターを使用します。 ただし、パラメーター化されたコンストラクターを使用するように指示できます。これにより、変更できないクラスまたは構造体を逆シリアル化できます。

  • クラスの場合、唯一のコンストラクターがパラメーター化されたコンストラクターである場合、そのコンストラクターが使用されます。

  • 構造体または複数のコンストラクターを持つクラスの場合は、[JsonConstructor] 属性を適用して使用するものを指定します。 属性が使用されていない場合、パブリック パラメーターなしのコンストラクターが存在する場合は常に使用されます。

    次の例では、[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 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"}
    

    .NET 7 以前のバージョンでは、[JsonConstructor] 属性はパブリック コンストラクターでのみ使用できます。

.NET 8 以降のバージョンでは、リフレクション モードでは、[JsonConstructor]でマークされた非パブリック コンストラクターがサポートされています。 .NET 11 以降では、ソース生成モードでもサポートされています。

パラメーター化されたコンストラクターのパラメーター名は、プロパティの名前と型と一致する必要があります。 一致では大文字と小文字は区別されません。また、[JsonPropertyName] を使用してプロパティの名前を変更する場合でも、コンストラクター パラメーターは実際のプロパティ名と一致している必要があります。 次の例では、TemperatureC プロパティの名前が JSON で celsius に変更されていますが、コンストラクター パラメーターの名前は 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 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"}

[JsonPropertyName]に加えて、次の属性はパラメーター化されたコンストラクターによる逆シリアル化をサポートします。

参照渡しのコンストラクターのパラメーター

.NET 11 以降では、in は、コンストラクター パラメーターで ref、out、ref readonly、および JsonSerializer 修飾子を使用する型をデシリアライズします。

パラメーター修飾子 逆シリアル化の動作
in、 ref、および ref readonly シリアライザーは、各パラメーターを名前でバインドし、型の一致に基になる要素型を使用します。
out シリアライザーは、パラメーターを JSON にバインドしません。 コンストラクターが割り当てる値は破棄されます。

次のコンストラクターでは、シリアライザーは JSON から temperatureC バインドします。 isValidバインドされません。

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

Visual Basicでは、ByRef コンストラクター パラメーターは、次の表に示すref動作に従います。

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

レコード

次の例に示すように、シリアル化と逆シリアル化の両方でレコードもサポートされます。

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

属性の property: ターゲットを使用して、任意の属性をプロパティ名に適用できます。 位置指定レコードの詳細については、C# 言語リファレンスで レコードに関する記事を参照してください。

非パブリック メンバーとプロパティ アクセサー

次の例に示すように、[JsonInclude] 属性を使用して、プロパティで非パブリック アクセサー の使用を有効にすることができます。

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

プライベート セッターにプロパティを含めることで、そのプロパティを逆シリアル化できます。

.NET 8 以降のバージョンでは、[JsonInclude] 属性を使用して、特定の型のシリアル化コントラクトに 非パブリック メンバーを選択することもできます。

.NET 11 以降では、ソース生成では、[JsonInclude]でマークするprivate、internal、およびprotectedメンバーがサポートされます。 また、[JsonInclude]でマークするプロパティのprivate、internal、およびprotectedアクセサーもサポートします。 ソース生成では、[ JsonConstructor] でマークされたアクセスできないコンストラクターもサポートされます。

手記

.NET 10 以前のバージョンでは、ソース生成では、privateまたはprotectedメンバーまたはアクセサーはサポートされていません。 [JsonInclude] 属性をメンバーまたはプロパティに適用しても、この制限は削除されません。 ソース生成では、 internal メンバーとアクセサーが、生成された JsonSerializerContextと同じアセンブリ内にある場合にのみサポートされます。 [JsonConstructor]でマークした場合でも、アクセスできないコンストラクターはサポートされません。

Init のみのプロパティ

System.Text.Json は、他の settable プロパティと同様に init専用プロパティを逆シリアル化します。 .NET 11 以降では、ソース生成セッターは、JSON ペイロードにプロパティが含まれている場合にのみ実行されます。 省略されたプロパティは、初期化子の値を保持します。

読み取り専用プロパティ

.NET 8 以降のバージョンでは、読み取り専用プロパティ、またはプライベートまたはパブリックのセッターがないプロパティも逆シリアル化できます。 プロパティが参照するインスタンスは変更できませんが、プロパティの型が変更可能な場合は変更できます。 たとえば、リストに要素を追加できます。 読み取り専用プロパティを逆シリアル化するには、オブジェクト作成処理の動作を replace ではなく populate に設定する必要があります。 たとえば、JsonObjectCreationHandlingAttribute 属性を使用してプロパティに注釈を付けることができます。

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

詳細については、「初期化されたプロパティを入力する 」を参照してください。

関連項目