在 C# 里处理 JSON,要么用 Newtonsoft.Json(Json.NET),要么用 .NET Core 自带的 System.Text.Json。JsonConvert.SerializeObject() 和 JsonSerializer.Deserialize<T>() 是标配,泛型反序列化、JsonProperty 特性、JsonConverter 自定义转换器,功能强大但复杂。
在 Python 里处理 JSON,内置 json 模块,json.dumps() 和 json.loads() 两个函数搞定一切。没有泛型,没有特性,没有转换器,就是字典和列表的序列化/反序列化。
当我第一次在 Python 里处理 JSON 的时候,我的内心是:"这...也太简单了吧?我的泛型呢?我的 JsonProperty 呢?我的 JsonSerializerOptions 呢?"
后来我才明白,Python 的 JSON 处理哲学是:JSON 就是字典的文本表示 ,不需要额外的映射。而 C# 的 JSON 处理哲学是:JSON 是一种数据格式,需要精确地映射到类型系统。
Python 处理 JSON 的思路就是:JSON 就是字典的文本表示,不需要额外的映射层。
基础语法对比
C# 版本:
using System.Text.Json;
// 序列化
var user = new { Name = "Alice", Age = 25 };
string json = JsonSerializer.Serialize(user);
// {"Name":"Alice","Age":25}
// 反序列化
string jsonStr = "{\"Name\":\"Alice\",\"Age\":25}";
var user = JsonSerializer.Deserialize<User>(jsonStr);
Python 版本:
import json
# 序列化
user = {"name": "Alice", "age": 25}
json_str = json.dumps(user)
# '{"name": "Alice", "age": 25}'
# 反序列化
json_str = '{"name": "Alice", "age": 25}'
user = json.loads(json_str)
# {"name": "Alice", "age": 25}
对比一下:
| 操作 | C# | Python |
|---|---|---|
| 序列化 | JsonSerializer.Serialize() |
json.dumps() |
| 反序列化 | JsonSerializer.Deserialize<T>() |
json.loads() |
| 类型 | 需要定义类 | 直接用字典 |
| 缩进 | WriteIndented = true |
indent=2 |
| 编码 | 默认 UTF-8 | 默认 UTF-8 |
Python 的 JSON 处理像在翻译字典------json.loads() 把 JSON 字符串变成字典,json.dumps() 把字典变成 JSON 字符串。C# 的 JSON 处理像在做数据映射------把 JSON 精确地映射到类的属性上。
缩进格式化
C# 版本:
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(user, options);
// {
// "Name": "Alice",
// "Age": 25
// }
Python 版本:
json_str = json.dumps(user, indent=2)
# {
# "name": "Alice",
# "age": 25
# }
Python 只需要 indent=2,C# 需要设置 JsonSerializerOptions。Python 更简洁。
命名策略
C# 的 JSON 命名策略:
// 默认 PascalCase
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string json = JsonSerializer.Serialize(user, options);
// {"name":"Alice","age":25}
// 自定义策略
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
Python 默认就是 snake_case,因为 Python 的变量命名习惯就是 snake_case:
# Python 的字典键就是变量名风格
user = {"name": "Alice", "age": 25}
json_str = json.dumps(user)
# '{"name": "Alice", "age": 25}'
C# 需要额外配置命名策略,Python 不需要。
特殊值处理
C# 的 JsonSerializerOptions:
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
Python 的 json.dumps 参数:
json_str = json.dumps(
user,
indent=2,
ensure_ascii=False, # 支持中文
skipkeys=True, # 跳过不可序列化的键
sort_keys=True # 按键排序
)
| 功能 | C# | Python |
|---|---|---|
| 忽略 null | DefaultIgnoreCondition |
separators 或手动过滤 |
| 中文支持 | 默认支持 | ensure_ascii=False |
| 键排序 | 手动实现 | sort_keys=True |
| 跳过不可序列化键 | 异常 | skipkeys=True |
自定义序列化
C# 的自定义转换器:
public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
}
}
// 使用
var options = new JsonSerializerOptions();
options.Converters.Add(new DateTimeConverter());
string json = JsonSerializer.Serialize(dateTime, options);
Python 的自定义序列化:
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime("%Y-%m-%d %H:%M:%S")
return super().default(obj)
# 使用
json_str = json.dumps({"time": datetime.now()}, cls=DateTimeEncoder)
C# 用泛型转换器,Python 用 JSONEncoder 子类。Python 的方式更简单,但 C# 的方式更类型安全。
JSON 路径查询
C# 需要第三方库(如 JsonPath)来查询 JSON 路径。
Python 有 jsonpath-ng 库:
from jsonpath_ng import parse
data = {"users": [{"name": "Alice", "age": 25}, {"name": "Bob", "age": 30}]}
expr = parse("users[*].name")
matches = [match.value for match in expr.find(data)]
# ["Alice", "Bob"]
流式处理
C# 的流式处理:
using var stream = File.OpenRead("large.json");
var data = await JsonSerializer.DeserializeAsync<List<User>>(stream);
Python 的流式处理:
import ijson # 需要安装
with open("large.json", "rb") as f:
for record in ijson.items(f, "users.item"):
process(record)
Python 的 ijson 可以流式解析大型 JSON 文件,不会一次性加载到内存。
JSON Schema 验证
C# 可以用 JsonSchema.Net 来验证 JSON:
var schema = JsonSchema.FromType<User>();
var isValid = schema.Validate(jsonStr);
Python 可以用 jsonschema:
from jsonschema import validate
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "number"}
}
}
validate(instance={"name": "Alice", "age": 25}, schema=schema)
迁移指南:C# 开发者最容易犯的错
-
忘记
json.loads()返回字典:Python 的 JSON 反序列化直接返回字典,不是对象 -
日期格式不一致 :Python 的
datetime序列化格式和 C# 不同 -
编码问题 :
ensure_ascii=False才能正确处理中文 -
键名大小写:Python 默认 snake_case,C# 默认 PascalCase
-
空值处理 :Python 的
None序列化为null,但反序列化时null变成None
坑点提醒
日期序列化 ------Python 默认不序列化 datetime 对象:
from datetime import datetime
json.dumps({"time": datetime.now()}) # TypeError: Object of type datetime is not JSON serializable
解决方案:
json.dumps({"time": datetime.now().isoformat()})
# 或者用自定义编码器
浮点数精度------JSON 的浮点数精度问题:
import json
json.dumps(0.1 + 0.2) # '0.30000000000000004'
这不是 Python 的问题,是 IEEE 754 浮点数的标准行为。C# 也有同样的问题。
大数字精度------JSON 的大整数精度:
import json
json.dumps(9007199254740993) # '9007199254740993',但 JavaScript 可能丢失精度
Python 的 int 没有上限,但 JavaScript 的 Number 有精度限制。处理大整数时要注意。
真实案例 :我有个同事从 C# 转 Python,写了一个 API 对接脚本。脚本从 C# 后端获取 JSON 数据,但 Python 反序列化后字典的键是 camelCase(如 userName),而 Python 代码用的是 snake_case(如 user_name)。后来用了一个 humps 库来自动转换键名才解决。
一句话总结
C# 的 JSON 处理像在做数据映射------把 JSON 精确地映射到类型系统;Python 的 JSON 处理像在翻译字典------JSON 就是字典的文本表示,简单直接。
下一篇咱们来聊聊单元测试------C# 的 xUnit/NUnit vs Python 的 pytest,两种语言的"测试哲学"又有啥不同。
📦 示例代码:C# 转 Python 全系列配套练习代码(含 48 章示例)
💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!