导读:本期聚焦于Ada创作的《C#怎么序列化和反序列化Dictionary?多种实现方式详解》,敬请观看详情。Dictionary是C#开发中常用的集合类型,但把它正确地序列化成JSON并还原回来,涉及不少细节。本文围绕C#怎么序列化和反序列化Dictionary这一问题,详细讲解Newtonsoft.Json和System.Text.Json两种主流方案的具体用法,包括字典嵌套、自定义键类型、非字符串键的处理方式,以及反序列化时常见的类型转换问题。同时还会对比两种JSON库在性能和功能上的差异,给出实际项目中的选型建议,并补充BinaryFormatter二进制序列化的注意事项,帮助你彻底掌握Dictionary的持久化和网络传输处理技巧。

在C#项目开发中,Dictionary是存放键值对数据最常用的集合类型。当我们需要把数据保存到文件、缓存或者通过网络传输时,就绕不开序列化这个环节。不过Dictionary的结构和普通类不同,它的键和值都可以是任意类型,直接序列化时常会遇到键类型不支持、嵌套结构解析失败等问题。这篇文章就来系统地讲讲C#中序列化和反序列化Dictionary的几种主流做法,并结合实际场景分析各自的适用范围。

C#怎么序列化和反序列化Dictionary?多种实现方式详解

使用Newtonsoft.Json序列化Dictionary

Newtonsoft.Json也就是大家常说的Json.NET,是.NET生态中使用最广泛的JSON库。它对Dictionary的支持非常成熟,几乎不需要额外配置就能完成序列化和反序列化。序列化时,字典的键会直接变成JSON对象的属性名,值则对应属性的值。

下面是一个基础示例,展示如何把一个字符串键的字典转成JSON字符串,再还原回来:

using Newtonsoft.Json;
using System.Collections.Generic;

var dict = new Dictionary<string, int>
{
    { "apple", 10 },
    { "banana", 25 },
    { "orange", 8 }
};

// 序列化
string json = JsonConvert.SerializeObject(dict, Formatting.Indented);

// 反序列化
var restored = JsonConvert.DeserializeObject<Dictionary<string, int>>(json);

Console.WriteLine(json);
Console.WriteLine(restored["banana"]); // 输出 25

这段代码执行后,JSON字符串的内容是一个对象形式,形如{"apple":10,"banana":25,"orange":8}。反序列化时只要明确指定泛型类型Dictionary<string, int>,Json.NET会自动完成类型匹配。

需要注意的一点是,如果字典的值是复杂类型,比如自定义类,反序列化同样没有障碍,Json.NET会递归处理嵌套结构。此外,JsonConvert.DeserializeObject在输入为空字符串时返回null,实际项目中最好加一层判空保护,避免后续代码抛出空引用异常。

使用System.Text.Json处理字典序列化

从.NET Core 3.0开始,微软推出了内置的System.Text.Json库,它以高性能、低分配著称,现在已经成为新项目的默认选择。用它处理Dictionary的基础用法和Json.NET类似,但细节上有一些差异,特别是非字符串键的处理上,两者行为并不一致。

先看基础用法:

using System.Text.Json;
using System.Collections.Generic;

var dict = new Dictionary<string, object>
{
    { "name", "张三" },
    { "age", 28 },
    { "scores", new List<int> { 90, 85, 77 } }
};

// 序列化
string json = JsonSerializer.Serialize(dict, new JsonSerializerOptions
{
    WriteIndented = true,
    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});

// 反序列化
var restored = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
Console.WriteLine(restored["name"]);

这里有一个中文开发者经常踩的坑:System.Text.Json默认会把非ASCII字符转义成Unicode编码,序列化结果里中文会变成\u5F20\u4E09这样的形式。虽然功能上不影响正确性,但可读性很差,也不利于排查问题。解决办法就是像上面那样设置JavaScriptEncoder.UnsafeRelaxedJsonEscaping,让中文原样输出。

另一个重要差异是非字符串键的支持。System.Text.Json在序列化时支持int、枚举等非字符串键的字典,会自动把键转成字符串。但反序列化回非字符串键的字典时,旧版本会抛异常,从.NET 5开始才加入了对键类型转换的支持。如果你的项目还在使用较老版本,遇到Dictionary<int, string>这类结构反序列化失败,可以考虑升级框架版本,或者改用Newtonsoft.Json。

嵌套字典和复杂结构的处理

实际业务中,字典往往会嵌套使用,比如一个配置系统可能用Dictionary<string, Dictionary<string, object>>来存储分组配置。这种结构的序列化本身没有难度,麻烦的地方在于反序列化时object类型的值会变成什么。

以Json.NET为例,反序列化到object时,JSON对象会变成JObject,数组变成JArray,这在后续取值时需要做类型判断。示例代码如下:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;

var config = new Dictionary<string, object>
{
    { "database", new Dictionary<string, object>
        {
            { "host", "127.0.0.1" },
            { "port", 3306 }
        }
    }
};

string json = JsonConvert.SerializeObject(config);
var restored = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

var db = restored["database"] as JObject;
string host = db["host"].ToString();
int port = db["port"].ToObject<int>();
Console.WriteLine($"{host}:{port}");

如果用的是System.Text.Json,情况稍有不同:反序列化到object后,JSON对象会变成JsonElement,需要通过GetProperty方法和GetInt32GetString这类强类型方法来取值。两种方式各有优劣,JObject的索引器写起来更顺手,JsonElement则在解析大JSON时内存占用更小。

对于结构相对固定的数据,更推荐的做法是定义具体的强类型模型类来接收反序列化结果,而不是依赖字典加动态解析。强类型能在编译期发现拼写错误,配合IDE的智能提示,代码的可维护性明显更好。字典更适合键名不确定的场景,比如从外部接口接收动态数据。

二进制序列化及其他方案对比

除了JSON,C#还支持二进制序列化。老的BinaryFormatter曾经是常见选择,但微软已经在官方文档中明确将其标记为不安全,指出它存在反序列化漏洞风险,攻击者可以构造恶意数据在反序列化时执行任意代码。因此在新的.NET版本中,这个类已经被标记为过时,编译时会直接报错,不建议在任何新项目中使用。

替代方案有几种。如果追求跨平台和可读性,JSON仍然是最稳妥的选择;如果追求体积和速度,可以考虑protobuf-net这类第三方二进制库,它对Dictionary有良好的支持,性能和压缩率都远超JSON:

using ProtoBuf;
using System.Collections.Generic;
using System.IO;

[ProtoContract]
public class CacheData
{
    [ProtoMember(1)]
    public Dictionary<string, string> Items { get; set; }
}

var data = new CacheData
{
    Items = new Dictionary<string, string>
    {
        { "key1", "value1" },
        { "key2", "value2" }
    }
};

using var stream = new MemoryStream();
Serializer.Serialize(stream, data);

stream.Position = 0;
var result = Serializer.Deserialize<CacheData>(stream);
Console.WriteLine(result.Items["key1"]);

在库的选型上,可以从几个维度来决策。性能方面,System.Text.Json在大多数基准测试中比Newtonsoft.Json快一到三倍,高并发场景下优势明显;功能方面,Json.NET更加灵活,对各种特殊类型的兼容性更好,老项目迁移成本低。如果项目基于.NET Core 3.0以上且没有特殊需求,优先使用System.Text.Json;如果历史代码大量依赖JObject、JToken这些API,继续使用Json.NET也没问题,两者完全可以共存。

总结一下,Dictionary的序列化在C#中并不复杂,核心在于根据字典的键值类型选择合适的库,并处理好中文字符编码、非字符串键、嵌套结构这三个常见的坑。掌握这些细节后,无论是做配置存储、缓存持久化还是接口数据传输,都能得心应手。

C#序列化Dictionary反序列化JSON序列化修改时间:2026-09-11 03:38:34

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260911/54434.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。