ZetCode

C# JSON

最后修改于 2025 年 5 月 13 日

本 C# JSON 教程演示了如何使用标准库的内置类来处理 JSON 数据。 JSON 是一种广泛使用的数据格式,用于在应用程序之间交换信息,尤其是在 Web 开发和 API 中。 了解如何在 C# 中解析、序列化和操作 JSON 对于构建与外部数据源或服务交互的现代 .NET 应用程序至关重要。

JSON

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。 它易于人类阅读和编写,同时可以被机器高效地解析和生成。 JSON 的官方 Internet 媒体类型是 application/json,JSON 文件通常使用 .json 扩展名。 JSON 支持基本数据类型,例如字符串、数字、布尔值、数组和对象,使其能够灵活地表示复杂的数据结构。

在本教程中,我们将重点介绍如何使用 C# 标准库处理 JSON。 此外,还有一个广泛使用的第三方库,名为 Json.NET,它为 JSON 处理提供了扩展的功能。 虽然出于性能和与 .NET 集成的考虑,建议大多数新项目使用 System.Text.Json,但 Json.NET 仍然因其高级功能和兼容性而广受欢迎。

System.Text.Json

System.Text.Json 命名空间为在 C# 中处理 JSON 提供了高性能、内存效率高且符合标准的解决方案。 它支持将对象序列化为 JSON 文本,以及将 JSON 文本反序列化为对象,并内置 UTF-8 优化以加快处理速度。 此命名空间包含在 .NET Core 3.0 及更高版本中,是现代 C# 应用程序中处理 JSON 的首选方式。

C# JSON 解析

JsonDocument.Parse 方法将流或字符串解析为 UTF-8 编码的数据,表示单个 JSON 值到 JsonDocument 中。 该流将被读取完毕,并且生成后的文档提供了一个类似 DOM 的 API,用于从 JSON 结构中导航和提取数据。 当您需要检查或操作 JSON 数据而无需将其直接映射到 C# 类型时,这非常有用。

Program.cs
using System.Text.Json;

string data = @" [ {""name"": ""John Doe"", ""occupation"": ""gardener""}, 
    {""name"": ""Peter Novak"", ""occupation"": ""driver""} ]";

using JsonDocument doc = JsonDocument.Parse(data);
JsonElement root = doc.RootElement;

Console.WriteLine(root);

var u1 = root[0];
var u2 = root[1];
Console.WriteLine(u1);
Console.WriteLine(u2);

Console.WriteLine(u1.GetProperty("name"));
Console.WriteLine(u1.GetProperty("occupation"));

Console.WriteLine(u2.GetProperty("name"));
Console.WriteLine(u2.GetProperty("occupation"));

在此示例中,我们解析一个简单的 JSON 字符串,其中包含一个对象数组。 每个对象代表一个具有名称和职业的用户。 我们使用 JsonDocument 解析字符串并访问根元素,该根元素是一个数组。 然后,我们使用提供的 API 提取单个元素及其属性。 这种方法在处理动态或未知的 JSON 结构时很有用。

using JsonDocument doc = JsonDocument.Parse(data);

我们将 JSON 字符串解析为 JsonDocument,这使我们可以使用类似 DOM 的 API 导航 JSON 结构。 当 JSON 的结构在编译时未知或者您只需要访问数据的一个子集时,这尤其有用。

JsonElement root = doc.RootElement;

我们使用 RootElement 属性获取对根元素的引用。 根元素可以是对象、数组或原始值,具体取决于 JSON 数据。

var u1 = root[0];
var u2 = root[1];
Console.WriteLine(u1);
Console.WriteLine(u2);

使用 [] 运算符,我们获得 JSON 文档的第一个和第二个子元素。 这使我们可以访问数组中的单个对象,并进一步检查它们的属性。

Console.WriteLine(u1.GetProperty("name"));
Console.WriteLine(u1.GetProperty("occupation"));

我们使用 GetProperty 获取元素的属性。 此方法从 JSON 对象中检索指定名称的属性值,使我们能够从数据中提取特定字段。

$ dotnet run
[ {"name": "John Doe", "occupation": "gardener"},
  {"name": "Peter Novak", "occupation": "driver"} ]
{"name": "John Doe", "occupation": "gardener"}
{"name": "Peter Novak", "occupation": "driver"}
John Doe
gardener
Peter Novak
driver

C# JSON 枚举

在此示例中,我们枚举根元素的内容,该根元素是一个 JSON 数组。 我们使用 EnumerateArray 方法迭代数组中的每个对象,然后使用 EnumerateObject 访问每个对象的所有属性。 这种方法对于处理 JSON 格式的数据集合(例如用户或项目列表)非常有用。

Program.cs
using System.Text.Json;

string data = @" [ {""name"": ""John Doe"", ""occupation"": ""gardener""}, 
    {""name"": ""Peter Novak"", ""occupation"": ""driver""} ]";

using var doc = JsonDocument.Parse(data);
JsonElement root = doc.RootElement;

var users = root.EnumerateArray();

while (users.MoveNext())
{
    var user = users.Current;
    System.Console.WriteLine(user);

    var props = user.EnumerateObject();

    while (props.MoveNext())
    {
        var prop = props.Current;
        Console.WriteLine($"{prop.Name}: {prop.Value}");
    }
}

我们使用 EnumerateArray 获取子元素的数组。 此方法返回一个枚举器,该枚举器允许我们循环遍历 JSON 数组中的每个元素,从而可以轻松地处理多个项目。

var users = root.EnumerateArray();

在 while 循环中,我们遍历元素数组。 对于每个元素,我们打印整个对象,然后枚举其属性。 这演示了如何遍历嵌套的 JSON 结构并从每个对象中提取详细信息。

while (users.MoveNext())
{
    var user = users.Current;
    Console.WriteLine(user);
...

在第二个 while 循环中,我们遍历每个元素的属性。 我们使用 EnumerateObject 访问 JSON 对象中的所有键值对,从而可以单独打印或处理每个属性。

var props = user.EnumerateObject();

while (props.MoveNext())
{
    var prop = props.Current;
    Console.WriteLine($"{prop.Name}: {prop.Value}");
}
$ dotnet run
{"name": "John Doe", "occupation": "gardener"}
name: John Doe
occupation: gardener
{"name": "Peter Novak", "occupation": "driver"}
name: Peter Novak
occupation: driver

C# JSON 序列化

在此示例中,我们使用 JsonSerializer.SerializeUser 对象转换为 JSON 字符串。 序列化是将对象或数据结构转换为可以轻松存储或传输的格式(例如 JSON)的过程。 这通常用于将数据发送到 Web API 或保存配置文件。

Program.cs
using System.Text.Json;

var user = new User("John Doe", "gardener", new MyDate(1995, 11, 30));

var json = JsonSerializer.Serialize(user);
Console.WriteLine(json);

record MyDate(int year, int month, int day);
record User(string Name, string Occupation, MyDate DateOfBirth);
$ dotnet run
{"Name":"John Doe","Occupation":"gardener",
    "DateOfBirth":{"year":1995,"month":11,"day":30}}

C# JSON 反序列化

JsonSerializer.Deserialize 方法将表示单个 JSON 值的文本解析为指定类型的实例。 这使您可以将从外部源接收的 JSON 数据转换为强类型的 C# 对象,从而更容易在应用程序中使用数据。

Program.cs
using System.Text.Json;

string json = @"{""Name"":""John Doe"", ""Occupation"":""gardener"",
    ""DateOfBirth"":{""year"":1995,""month"":11,""day"":30}}";

var user = JsonSerializer.Deserialize<User>(json);

Console.WriteLine(user);

Console.WriteLine(user?.Name);
Console.WriteLine(user?.Occupation);
Console.WriteLine(user?.DateOfBirth);

record MyDate(int year, int month, int day);
record User(string Name, string Occupation, MyDate DateOfBirth);

该示例将 JSON 字符串解析为 User 类型的实例。 反序列化后,您可以像使用任何 C# 类一样访问对象的属性,从而可以对数据进行类型安全的处理。

C# JsonSerializerOptions

使用 JsonSerializerOptions,我们可以使用一些选项控制序列化过程。 例如,WriteIndented 选项启用美化打印,使输出更具可读性。 其他选项允许自定义属性命名、处理空值等。

Program.cs
using System.Text.Json;

var words = new Dictionary<int, string>
{
    {1, "sky"},
    {2, "cup"},
    {3, "odd"},
    {4, "cloud"},
    {5, "forest"},
    {6, "warm"},
};

var r = JsonSerializer.Serialize(words, 
    new JsonSerializerOptions { WriteIndented = true });

Console.WriteLine(r);

Console.WriteLine("---------------------");

var d = JsonSerializer.Deserialize<Dictionary<int, string>>(r);

foreach (var (k, v) in d!)
{
    Console.WriteLine($"{k}: {v}");
}

设置 WriteIndented 选项后,我们启用缩进进行美化打印。 这对于调试或当 JSON 输出需要人为可读时非常有用,例如在配置文件或日志中。

$ dotnet run
{
  "1": "sky",
  "2": "cup",
  "3": "odd",
  "4": "cloud",
  "5": "forest",
  "6": "warm"
}
---------------------
1: sky
2: cup
3: odd
4: cloud
5: forest
6: warm

C# Utf8JsonWriter

在此示例中,我们创建一个新对象,并使用 Utf8JsonWriter 将其写入 JSON 字符串。 此类提供了一种高性能、仅向前 API,用于编写 UTF-8 编码的 JSON 文本。 它适用于需要动态生成 JSON 或需要对输出进行细粒度控制的场景。

Program.cs
using System.Text.Json;
using System.Text;

using var ms = new MemoryStream();
using var writer = new Utf8JsonWriter(ms);

writer.WriteStartObject();
writer.WriteString("name", "John Doe");
writer.WriteString("occupation", "gardener");
writer.WriteNumber("age", 34);
writer.WriteEndObject();
writer.Flush();

string json = Encoding.UTF8.GetString(ms.ToArray());

Console.WriteLine(json);
$ dotnet run
{"name":"John Doe","occupation":"gardener","age":34}

在此示例中,我们将 JSON 字符串写入文件。 使用 JsonWriterOptionsIndented 选项美化数据。 美化打印使 JSON 更易于阅读和维护,尤其适用于配置文件或用于手动检查的数据。

Program.cs
using System.Text.Json;

string data = @" [ {""name"": ""John Doe"", ""occupation"": ""gardener""}, 
    {""name"": ""Peter Novak"", ""occupation"": ""driver""} ]";

JsonDocument jdoc = JsonDocument.Parse(data);

var fileName = @"data.json";
using FileStream fs = File.OpenWrite(fileName);

using var writer = new Utf8JsonWriter(fs, new JsonWriterOptions { Indented = true });
jdoc.WriteTo(writer);
$ cat data.json 
[
    {
    "name": "John Doe",
    "occupation": "gardener"
    },
    {
    "name": "Peter Novak",
    "occupation": "driver"
    }
]

C# JSON Utf8JsonReader

在此示例中,我们使用 Utf8JsonReader 从文件中读取 JSON 数据。 它提供了一个低级别的 API,用于逐个令牌地读取 JSON 数据。 这对于高效处理大型 JSON 文件或流非常有用,因为它避免了将整个文档加载到内存中。

Program.cs
using System.Text.Json;

var fileName = @"/home/user7/data.json";
byte[] data = File.ReadAllBytes(fileName);
Utf8JsonReader reader = new Utf8JsonReader(data);

while (reader.Read())
{
    switch (reader.TokenType)
    {
        case JsonTokenType.StartObject:
            Console.WriteLine("-------------");
            break;
        case JsonTokenType.EndObject:
            break;
        case JsonTokenType.StartArray:
        case JsonTokenType.EndArray:
            break;
        case JsonTokenType.PropertyName:
            Console.Write($"{reader.GetString()}: ");
            break;
        case JsonTokenType.String:
            Console.WriteLine(reader.GetString());
            break;
        default:
            throw new ArgumentException();

    }
}
$ dotnet run
-------------
name: John Doe
occupation: gardener
-------------
name: Peter Novak
occupation: driver

C# JSON 异步解析

该示例读取 .NET Core 框架的所有版本,这些版本以 JSON 字符串的形式在项目 Github 存储库上提供。 它演示了如何使用 JsonDocument.ParseAsync 从流中异步读取和解析 JSON 数据,这对于处理网络或文件 I/O 的响应式应用程序非常重要。

Program.cs
using System.Text.Json;

using var httpClient = new HttpClient();

var url = "https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases-index.json";
var ts = await httpClient.GetStreamAsync(url);

using var resp = await JsonDocument.ParseAsync(ts);

var root = resp.RootElement.GetProperty("releases-index");

var elems = root.EnumerateArray();

while (elems.MoveNext())
{
    var node = elems.Current;
    Console.WriteLine(node);
}

C# HttpClient GetFromJsonAsync

GetFromJsonAsync 方法将 GET 请求发送到指定的 URL,并返回异步操作中反序列化为 JSON 的响应正文所产生的值。 此方法简化了从 Web API 获取和解析 JSON 数据的过程,从而可以轻松地将外部数据源集成到 C# 应用程序中。

该方法是 System.Net.Http.Json 中的扩展方法。

Program.cs
using System.Text.Json.Serialization;
using System.Net.Http.Json;

using var client = new HttpClient();

var url = "http://webcode.me/users.json";
var data = await client.GetFromJsonAsync<Users>(url);

if (data != null)
{
    foreach (var user in data.users)
    {
        Console.WriteLine(user);
    }
}

class Users
{
    public List<User> users { get; set; } = new();
}

class User
{
    [JsonPropertyName("id")]
    public int Id { get; set; }

    [JsonPropertyName("first_name")]
    public string FirstName { get; set; } = string.Empty;

    [JsonPropertyName("last_name")]
    public string LastName { get; set; } = string.Empty;

    [JsonPropertyName("email")]
    public string Email { get; set; } = string.Empty;

    public override string ToString()
    {
        return $"User {{ {Id}| {FirstName} {LastName}| {Email} }}";
    }
}

我们创建一个对 JSON 资源的异步 http 请求。 JSON 数据被序列化为 User 对象列表,从而可以轻松地在 C# 中迭代和处理数据。

var data = await client.GetFromJsonAsync<Users>(url);

GetFromJsonAsync 是一种方便的方法,可将 JSON 资源转换为 C# 集合。 它处理 HTTP 请求、响应、反序列化和错误处理,从而减少了样板代码并提高了工作效率。

class Users
{
    public List<User> users { get; set; } = new();
}

我们需要为 List 集合创建一个特定的类。 此类充当反序列化数据的容器,与 JSON 响应的结构相匹配。

class User
{
    [JsonPropertyName("id")]
    public int Id { get; set; }

    [JsonPropertyName("first_name")]
    public string FirstName { get; set; } = string.Empty;

    [JsonPropertyName("last_name")]
    public string LastName { get; set; } = string.Empty;

    [JsonPropertyName("email")]
    public string Email { get; set; } = string.Empty;

    public override string ToString()
    {
        return $"User {{ {Id}| {FirstName} {LastName}| {Email} }}";
    }
}

JSON 字段使用 JsonPropertyName 属性映射到类属性。 这确保了 C# 属性对应于正确的 JSON 字段,即使名称不同也是如此。 重写 ToString() 提供了一种显示对象数据的便捷方法。

$ dotnet run
User { 1| Robert Schwartz| rob23@gmail.com }
User { 2| Lucy Ballmer| lucyb56@gmail.com }
User { 3| Anna Smith| annasmith23@gmail.com }
User { 4| Robert Brown| bobbrown432@yahoo.com }
User { 5| Roger Bacon| rogerbacon12@yahoo.com }

来源

JSON 序列化和反序列化

在本文中,我们使用了 C# 中的 JSON 数据。

作者

我的名字是 Jan Bodnar,我是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程