如何通过 System.Text.Json 使用不可变类型和非公共访问器
本文演示如何将不可变类型、公共参数化构造函数和非公共访问器与 System.Text.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 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"}
参数化构造函数的参数名称必须与属性名称和类型匹配。 匹配不区分大小写,即使使用 [JsonPropertyName] 重命名属性,构造函数参数也必须与实际属性名称匹配。 在下面的示例中,TemperatureC
属性的名称在 JSON 中更改为 celsius
,但构造函数参数仍然名为 temperatureC
:
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"}
除了 [JsonPropertyName]
,以下属性也支持使用参数化构造函数进行反序列化:
还支持 C# 9 记录,如以下示例中所示:
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! }
可以使用 property:
特性上的目标,将任何特性应用于属性名称。 有关位置记录的详细信息,请参阅 C# 语言参考中关于记录的文章。
对于因其所有属性资源库都是非公共的而不可变的类型,请参阅以下部分。
.NET Core 3.1 不支持 JsonConstructorAttribute
和 C# 9 记录。
非公共属性访问器
System.Text.Json
不支持将非公共属性序列化。 但你可以使用 [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 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"}
.NET Core 3.1 不支持非公共属性访问器。 有关详细信息,请参阅从 Newtonsoft.Json 迁移。
请参阅
- System.Text.Json 概述
- 如何对 JSON 进行序列化和反序列化
- 对 JsonSerializerOptions 实例进行实例化
- 启用不区分大小写的匹配
- 自定义属性名称和值
- 忽略属性
- 允许无效的 JSON
- 处理溢出 JSON,或者使用 JsonElement 或 JsonNode
- 保留引用并处理循环引用
- 多态序列化
- 从 Newtonsoft.Json 迁移到 System.Text.Json
- 自定义字符编码
- 使用 DOM、Utf8JsonReader 和 Utf8JsonWriter
- 编写用于 JSON 序列化的自定义转换器
- DateTime 和 DateTimeOffset 支持
- 如何使用源生成
- 受支持的集合类型
- System.Text.Json API 参考
- System.Text.Json.Serialization API 参考