語言
本文說明如何使用 System.Text.Json 命名空間來處理溢位 JSON。 它也會示範如何還原序列化成 JsonElement 或 JsonNode,作為其他情境下的替代做法;在這些情境中,目標型別可能無法與要還原序列化的 JSON 內容完全相符。
處理溢出 JSON
反序列化時,您可能會在 JSON 中收到未由目標型別屬性表示的資料。 例如,假設您的目標類型如下:
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
Public Class WeatherForecast
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
End Class
而要還原序列化的 JSON 如下:
{
"Date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
如果您將顯示的 JSON 還原序列化為所顯示類型,則 DatesAvailable 和 SummaryWords 屬性會無處可去,且會遺失。 若要擷取像這些屬性這樣的額外資料,請將 [JsonExtensionData] 屬性套用到屬性或欄位。 請使用下列其中一種受支援的宣告:
- 宣告擴充資料成員為 JsonObject。
- 宣告擴充資料成員為
IDictionary<string, object>。 - 宣告擴充資料成員為
IDictionary<string, JsonElement>。 - 在 .NET 11 及更新版本中,將擴充資料成員宣告為
IReadOnlyDictionary<string, object>。 - 在 .NET 11 及更新版本中,將擴充資料成員宣告為
IReadOnlyDictionary<string, JsonElement>。
對於可變的字典擴充資料,請使用任何可指派給其中一個受支援的 IDictionary 介面的類型,例如 Dictionary<string, JsonElement>。 唯讀宣告必須使用 IReadOnlyDictionary 上述精確的介面類型之一。
public class WeatherForecastWithExtensionData
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
}
Public Class WeatherForecastWithExtensionData
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
<JsonExtensionData>
Public Property ExtensionData As Dictionary(Of String, Object)
End Class
對於 IReadOnlyDictionary 擴充資料成員,.NET 11 會將其具現化為 Dictionary<string, object> 或 Dictionary<string, JsonElement>。 該成員必須是可寫的,因為序列化器會以現有值初始化新的字典,然後再將其指派回該成員。 如果輸入的 JSON 屬性重複了現有的鍵,則該輸入值會勝出。
下表顯示將稍早所顯示 JSON 還原序列化為此範例類型的結果。 額外資料會成為 ExtensionData 屬性的鍵值配對:
| 屬性 | 值 | 備註 |
|---|---|---|
Date |
"8/1/2019 12:00:00 AM -07:00" |
|
TemperatureCelsius |
0 |
因為大小寫不一致(JSON 中的 temperatureCelsius),所以不會設定該屬性。 |
Summary |
"Hot" |
|
ExtensionData |
"temperatureCelsius": 25,"DatesAvailable": ["2019-08-01T00:00:00-07:00","2019-08-02T00:00:00-07:00"],"SummaryWords": ["Cool","Windy","Humid"] |
由於大小寫不一致,temperatureCelsius 會被視為多餘項,並成為字典中的鍵值組。 JSON 中的每個額外陣列都會變成機碼值組,並將陣列當作值物件。 |
將目標物件序列化時,擴充資料索引鍵/值組會成為 JSON 屬性,如同它們在傳入的 JSON 中一樣:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 0,
"Summary": "Hot",
"temperatureCelsius": 25,
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
請注意,ExtensionData 屬性名稱不會出現在 JSON 中。 此行為可讓 JSON 在往返傳遞過程中,不會遺失任何原本無法反序列化的額外資料。
從 .NET 11 開始,序列化會將 JsonObject 擴充資料攤平至其所屬的 JSON 物件中。
JsonObject 屬性會直接出現在其所在的物件中,而不是出現在 extension-data 成員名稱之下。
下列範例顯示從 JSON 到還原序列化物件,並返回 JSON 的來回行程:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace RoundtripExtensionData
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
}
public class Program
{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""temperatureCelsius"": 25,
""Summary"": ""Hot"",
""SummaryField"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}";
WeatherForecast weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString)!;
var serializeOptions = new JsonSerializerOptions { WriteIndented = true };
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
Console.WriteLine($"JSON output:\n{jsonString}\n");
}
}
}
// output:
//JSON output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 0,
// "Summary": "Hot",
// "temperatureCelsius": 25,
// "SummaryField": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00-07:00",
// "2019-08-02T00:00:00-07:00"
// ],
// "SummaryWords": [
// "Cool",
// "Windy",
// "Humid"
// ]
//}
反序列化為 JsonElement 或 JsonNode
如果您只想要靈活地決定特定屬性可接受的 JSON,替代方式是還原序列化為 JsonElement 或 JsonNode。 任何有效的 JSON 屬性都可以還原序列化為 JsonElement 或 JsonNode。 選擇 JsonElement 建立 不可變 的物件,或 JsonNode 建立 可變 物件。
下列範例示範針對包含 JsonElement 和 JsonNode 類型屬性的類別,如何從 JSON 轉換後再轉回 JSON。
using System.Text.Json;
using System.Text.Json.Nodes;
namespace RoundtripJsonElementAndNode
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public JsonElement DatesAvailable { get; set; }
public JsonNode? SummaryWords { get; set; }
}
public class Program
{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}";
WeatherForecast? weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString);
var serializeOptions = new JsonSerializerOptions { WriteIndented = true };
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
Console.WriteLine(jsonString);
}
}
}
// output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00-07:00",
// "2019-08-02T00:00:00-07:00"
// ],
// "SummaryWords": [
// "Cool",
// "Windy",
// "Humid"
// ]
//}