C# JSON 操作完全指南:从基础到高级

ModelEngine·创作计划征文活动 10w+人浏览 1.7k人参与

在现代 C# 开发中,JSON(JavaScript Object Notation)已成为数据交换和配置存储的事实标准。无论是与前端 API 交互、读写配置文件,还是进行服务间通信,熟练处理 JSON 都是一项必备技能。

本文将全面覆盖 C# 中 JSON 处理的核心知识,重点讲解两个主流库的使用:Newtonsoft.JsonJson.NET)和 .NET Core 3.0+ 内置的 System.Text.Json。我将从基础概念讲起,逐步深入到高级场景,并提供实用的最佳实践和问题排查方案。

一、JSON 数据格式详解

1. JSON 的基本结构

JSON 数据格式主要有两种核心结构:

  • 对象(Object):一个无序的键 / 值对集合,以 { 开始,} 结束。每个键后跟 :,键 / 值对之间用 , 分隔。
  • 数组(Array):一个有序的值集合,以 [ 开始,] 结束,值之间用 , 分隔。

2. JSON 的数据类型

JSON 支持以下数据类型:

  • 字符串(String):双引号包围的 Unicode 字符序列(如 "name": "张三")。
  • 数字(Number):整数、浮点数(正数、负数、零,如 "age": 30)。
  • 布尔值(Boolean)true 或 false(如 "isStudent": false)。
  • null:表示空值(如 "score": null)。
  • 对象(Object):嵌套的键 / 值对集合(如 "address": { "city": "北京" })。
  • 数组(Array):有序的值列表(如 "hobbies": ["篮球", "编程"])。

3. JSON 示例

{
  "name": "张三",
  "age": 30,
  "isStudent": false,
  "hobbies": ["篮球", "足球", "阅读"],
  "address": {
    "street": "123 Main Street",
    "city": "北京",
    "country": "中国"
  },
  "score": null
}

二、核心依赖库选择与准备

在 C# 中处理 JSON,你主要有两个选择:

  1. Newtonsoft.Json (Json.NET):功能强大、生态成熟、兼容性好,是 .NET Framework 和早期 .NET Core 项目的首选。
  2. System.Text.Json:.NET Core 3.0+ /.NET 5+ 内置的高性能库,无需额外依赖,是新项目的推荐选择。

安装依赖

  • 对于 Newtonsoft.Json:在 Visual Studio 中,右键点击项目 -> 管理 NuGet 程序包 -> 搜索 Newtonsoft.Json -> 安装最新稳定版。或使用 Package Manager Console:

Install-Package Newtonsoft.Json
  • 对于 System.Text.Json:它已内置在 .NET Core 3.0, .NET 5, .NET 6, .NET 7, .NET 8 等框架中,无需安装任何额外包。只需在代码中引用 System.Text.Json 命名空间即可。

三、基础概念:JSON 与 C# 对象的映射关系

JSON 的数据结构与 C# 的数据类型可以天然地对应起来,这是序列化和反序列化的基础。

JSON 数据类型C# 对应数据类型 (示例)
对象 (Object)classstruct
数组 (Array)List<T>T[]
字符串 (String)string
数字 (Number)intlongdoubledecimal
布尔值 (Boolean)bool
nullnull (对应可空类型,如 int?string)

示例:定义映射 JSON 的 C# 实体类

假设我们有如下 JSON 数据:

{
  "Name": "张三",
  "Age": 25,
  "IsStudent": false,
  "Hobbies": ["篮球", "编程"],
  "Address": {
    "City": "北京",
    "Street": "中关村大街"
  },
  "Score": 95.5
}

我们可以定义对应的 C# 类:

// 嵌套对象 Address
public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

// 主对象 User
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool IsStudent { get; set; }
    public List<string> Hobbies { get; set; } // 数组对应 List<string>
    public Address Address { get; set; }     // 嵌套对象
    public decimal? Score { get; set; }      // 可空类型,对应 JSON 的 null
}

四、基础操作:序列化与反序列化

序列化(Serialization)是将 C# 对象转换为 JSON 字符串的过程。反序列化(Deserialization)是将 JSON 字符串转换回 C# 对象的过程。

1. 使用 Newtonsoft.Json

序列化 (Object to JSON)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 1. 构建 C# 对象实例
        var user = new User
        {
            Name = "张三",
            Age = 25,
            IsStudent = false,
            Hobbies = new List<string> { "篮球", "编程" },
            Address = new Address { City = "北京", Street = "中关村大街" },
            Score = 95.5m
        };

        // 2. 序列化
        // Formatting.Indented 用于生成带缩进的、可读性好的 JSON 字符串
        string jsonString = JsonConvert.SerializeObject(user, Formatting.Indented);

        // 3. 输出结果
        Console.WriteLine(jsonString);
    }
}

输出结果将是格式化后的 JSON 字符串,与上文示例一致。

反序列化 (JSON to Object)
using Newtonsoft.Json;
using System;

class Program
{
    static void Main()
    {
        // 1. JSON 字符串
        string jsonString = @"{
          ""Name"": ""李四"",
          ""Age"": 30,
          ""IsStudent"": true,
          ""Hobbies"": [""读书"", ""跑步""],
          ""Address"": {
            ""City"": ""上海"",
            ""Street"": ""外滩大道""
          },
          ""Score"": null
        }";

        // 2. 反序列化
        User user = JsonConvert.DeserializeObject<User>(jsonString);

        // 3. 访问对象属性
        Console.WriteLine($"姓名: {user.Name}");
        Console.WriteLine($"年龄: {user.Age}");
        Console.WriteLine($"城市: {user.Address.City}");
        Console.WriteLine($"分数: {user.Score ?? "无分数"}"); // 处理可空类型
    }
}

2. 使用 System.Text.Json

序列化 (Object to JSON)
using System;
using System.Collections.Generic;
using System.Text.Json;

class Program
{
    static void Main()
    {
        // 1. 构建 C# 对象实例 (同上)
        var user = new User
        {
            Name = "张三",
            Age = 25,
            IsStudent = false,
            Hobbies = new List<string> { "篮球", "编程" },
            Address = new Address { City = "北京", Street = "中关村大街" },
            Score = 95.5m
        };

        // 2. 配置序列化选项
        var options = new JsonSerializerOptions
        {
            WriteIndented = true, // 格式化输出
            // 解决中文被转义为 Unicode 的问题
            Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
        };

        // 3. 序列化
        string jsonString = JsonSerializer.Serialize(user, options);

        // 4. 输出结果
        Console.WriteLine(jsonString);
    }
}
反序列化 (JSON to Object)
using System;
using System.Text.Json;

class Program
{
    static void Main()
    {
        // 1. JSON 字符串 (同上)
        string jsonString = @"{
          ""Name"": ""李四"",
          ""Age"": 30,
          ""IsStudent"": true,
          ""Hobbies"": [""读书"", ""跑步""],
          ""Address"": {
            ""City"": ""上海"",
            ""Street"": ""外滩大道""
          },
          ""Score"": null
        }";

        // 2. 配置反序列化选项
        var options = new JsonSerializerOptions
        {
            // 忽略 JSON 键名的大小写,增强兼容性
            PropertyNameCaseInsensitive = true 
        };

        // 3. 反序列化
        User user = JsonSerializer.Deserialize<User>(jsonString, options);

        // 4. 访问对象属性 (同上)
        Console.WriteLine($"姓名: {user.Name}");
        Console.WriteLine($"爱好: {string.Join(", ", user.Hobbies)}");
    }
}

五、高级场景处理

1. 键名映射(JSON 与 C# 属性名不一致)

当 JSON 键名(如 user_name)与 C# 命名规范(如 UserName)不一致时,需要显式指定映射关系。

  • Newtonsoft.Json 使用 [JsonProperty] 特性:

public class User
{
    [JsonProperty("user_name")]
    public string Name { get; set; }

    [JsonProperty("user_age")]
    public int Age { get; set; }
}

  • System.Text.Json 使用 [JsonPropertyName] 特性:

    using System.Text.Json.Serialization;
    
    public class User
    {
        [JsonPropertyName("user_name")]
        public string Name { get; set; }
    
        [JsonPropertyName("user_age")]
        public int Age { get; set; }
    }
    

2. 忽略指定属性

序列化时,有时需要忽略某些敏感信息(如密码)或无需传输的属性。

  • Newtonsoft.Json 使用 [JsonIgnore] 特性:

    public class User
    {
        public string Name { get; set; }
        
        [JsonIgnore]
        public string Password { get; set; } // 序列化时将被忽略
    }
    
  • System.Text.Json 使用 [JsonIgnore] 特性:

    using System.Text.Json.Serialization;
    
    public class User
    {
        public string Name { get; set; }
    
        [JsonIgnore]
        public string Password { get; set; } // 序列化时将被忽略
    }
    

3. 处理日期时间

JSON 本身没有日期时间类型,通常以字符串("2024-05-20")或时间戳(1716220800000)表示。

  • Newtonsoft.Json

    var user = new { BirthDate = new DateTime(2000, 1, 1) };
    
    // 序列化为指定格式的字符串
    string json = JsonConvert.SerializeObject(user, new JsonSerializerSettings
    {
        DateFormatString = "yyyy-MM-dd",
        Formatting = Formatting.Indented
    });
    // 输出: {"BirthDate":"2000-01-01"}
    
  • System.Text.Json (.NET 6+)

    var user = new { BirthDate = new DateTime(2000, 1, 1) };
    
    var options = new JsonSerializerOptions
    {
        WriteIndented = true,
        DateFormatString = "yyyy-MM-dd" // 直接指定日期格式
    };
    
    string json = JsonSerializer.Serialize(user, options);
    // 输出: {"BirthDate":"2000-01-01"}
    

4. 动态解析 JSON(无需实体类)

在不确定 JSON 结构或结构频繁变化时,可以将其解析为动态对象或字典。

  • Newtonsoft.Json - 动态对象 (dynamic)

    string jsonString = @"{ ""name"": ""王五"", ""age"": 28 }";
    
    dynamic dynamicUser = JsonConvert.DeserializeObject(jsonString);
    Console.WriteLine(dynamicUser.name); // 输出: 王五
    Console.WriteLine(dynamicUser.age);  // 输出: 28
    
  • System.Text.Json - JsonDocument (推荐)JsonDocument 提供了一种高效、低分配的方式来读取 JSON 数据,适合临时查询。

    string jsonString = @"{ ""name"": ""王五"", ""age"": 28, ""address"": { ""city"": ""广州"" } }";
    
    // 使用 'using' 语句确保资源被释放
    using JsonDocument doc = JsonDocument.Parse(jsonString);
    JsonElement root = doc.RootElement;
    
    string name = root.GetProperty("name").GetString();
    int age = root.GetProperty("age").GetInt32();
    string city = root.GetProperty("address").GetProperty("city").GetString();
    
    Console.WriteLine($"{name}, {age}, {city}"); // 输出: 王五, 28, 广州
    

5. 处理 JSON 文件

读写 JSON 配置文件是常见需求。

using System.IO;
using Newtonsoft.Json; // 或 using System.Text.Json;

public class Config
{
    public string ApiKey { get; set; }
    public int Timeout { get; set; }
}

// 写入 JSON 文件
var config = new Config { ApiKey = "123456", Timeout = 30 };
string json = JsonConvert.SerializeObject(config, Formatting.Indented);
File.WriteAllText("appsettings.json", json, Encoding.UTF8);

// 读取 JSON 文件
string jsonFromFile = File.ReadAllText("appsettings.json", Encoding.UTF8);
Config loadedConfig = JsonConvert.DeserializeObject<Config>(jsonFromFile);
Console.WriteLine(loadedConfig.ApiKey);

System.Text.Json 的用法类似,只需替换 JsonConvert 为 JsonSerializer 并使用相应的 Options

六、常见问题与注意事项

  1. 中文乱码

    • Newtonsoft.Json 默认处理较好。
    • System.Text.Json 需要配置 Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping 来避免中文被转义为 Unicode。
    • 读写文件时,始终指定编码为 Encoding.UTF8
  2. 可空类型

    • JSON 中的 null 值必须映射到 C# 的可空类型(如 int?stringdecimal?),否则反序列化会失败。
  3. 键名大小写

    • Newtonsoft.Json 默认忽略大小写。
    • System.Text.Json 默认区分大小写,建议在反序列化时设置 PropertyNameCaseInsensitive = true 以提高兼容性。
  4. 循环引用

    • 当对象之间存在循环引用(A 包含 B,B 包含 A)时,直接序列化会导致无限递归和内存溢出。
    • Newtonsoft.Jsonnew JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }
    • System.Text.Json (.NET 5+):new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles }
  5. 枚举处理

    • 默认情况下,枚举会被序列化为其 underlying type(通常是 int)。
    • 若需序列化为字符串,需使用特性:
      • Newtonsoft.Json[JsonConverter(typeof(StringEnumConverter))]
      • System.Text.Json[JsonConverter(typeof(JsonStringEnumConverter))]

七、库的选择建议

特性Newtonsoft.Json (Json.NET)System.Text.Json
性能良好优秀 (通常更快,内存占用更低)
依赖需要安装 NuGet 包内置,无需额外依赖
.NET 版本.NET Framework, .NET Core, .NET 5+.NET Core 3.0+, .NET 5+
功能丰富度极高,API 灵活,支持大量高级场景,.NET 6+ 版本功能已非常完善
生态与社区非常成熟,问题解决方案多快速成长,官方支持力度大

选择建议:

  • 新项目 (.NET 5+):优先选择 System.Text.Json。它性能优异,与框架深度集成,是未来的发展方向。
  • 维护旧项目 (.NET Framework):继续使用 Newtonsoft.Json,它能提供最稳定和全面的支持。
  • 需要复杂自定义序列化逻辑Newtonsoft.Json 的 API 可能更灵活,更容易实现某些复杂场景。但 System.Text.Json 在最新版本中也在不断追赶。

总结

本文详细介绍了 C# 中处理 JSON 的两大主流库 Newtonsoft.Json 和 System.Text.Json。从环境准备、基础概念、核心的序列化与反序列化操作,到键名映射、日期处理等高级场景,再到常见问题排查和库的选择建议,都进行了全面的覆盖。

希望对大家有所帮助。感谢大家的关注和点赞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值