正文
这时,我们经常看到一种内容:
{
"meeting_id": "m42",
"title": "项目例会",
"duration_minutes": 30,
"online": false,
"participants": ["小林", "小周"],
"room": null
}
这是一份教学用的会议数据,不是从真实账户取得的结果。虽然它看起来像程序里的字典,但首先需要认识:它采用的是 JSON 格式。
JSON 是一种轻量的数据交换格式,用少量通用规则表示结构化数据,让不同程序能够按约定写出和读取。 全称 JavaScript Object Notation,名称与 JavaScript 有关,但使用它不要求双方都采用 JavaScript。JSON 的数据结构与基本写法
在这份数据里,最外面的花括号表示一个 JSON 对象。对象由名称与值组成:meeting_id 是名称,m42 是对应的字符串值;名称和值之间用冒号连接,不同成员之间用逗号分隔。
title 也是字符串,duration_minutes 则是数字。数字 30 与字符串“30”不同:前者可以在约定允许时直接参与数值计算,后者表达一段文字。我们选择数字表示会议时长,是因为这里约定它的单位为分钟。
online 的值 false 表示布尔值“假”。participants 使用方括号,表示一个数组,其中的两项都是字符串。数组适合表达按顺序排列的一组值,也可以保存嵌套的对象或数组。
room 的值 null 则表示一个明确的空值。它没有告诉我们会议室一定不存在,也没有自动说明“线上会议无需地点”。这些业务含义需要由接口双方约定,不能只靠 null 这个写法推出来。
同样,缺少 room 成员,与成员存在但值为 null,是两种不同的数据情况。程序可能需要区分“这次没有提供这个字段”和“明确提供了空值”,例如更新记录时,这两种情况可能对应不同处理。
JSON 的基本值包括对象、数组、字符串、数字、布尔值和 null。对象中的名称必须是字符串,标准写法使用双引号。true、false、null 都采用小写,不能直接换成 Python 的 True、False、None。
这也是为什么“看起来像字典”还不够。用单引号写一段 Python 字典,或者在最后一项后面加逗号,都可能是 Python 接受的表达形式,却不符合标准 JSON。标准 JSON 也没有普通注释语法。它约定的是数据表示规则,不会赋予字段业务含义;字段名称和单位仍然需要接口双方协商。JSON 的语法与语义范围
接下来要认识的是“序列化”。
程序运行时,会议记录可能保存在 Python 字典里。这个字典是程序内存中的数据结构,另一个使用不同语言的服务,不能直接拿到它的内存内容继续使用。
序列化,就是按照选定格式,把程序中的数据转换成适合保存或传输的表示;反序列化,则按相应规则读取这种表示,转换成程序能够处理的数据。 JSON 是可选格式之一,序列化也可以采用其他文本格式或二进制格式。
以 Python 为例,可以这样完成一次转换:
import json
meeting = {"meeting_id": "m42", "title": "项目例会", "online": False}
text = json.dumps(meeting, ensure_ascii=False)
restored = json.loads(text)
import json 导入标准库的 JSON 工具。meeting 是 Python 字典,其中的 False 是 Python 布尔值。json.dumps 把可支持的数据转换成 JSON 文本,结果保存在 text 中;json.loads 再读取这段文本,得到 restored。
按照这段示例的规则,text 中的布尔值会写成 JSON 的 false,restored 中对应值则回到 Python 的 False。ensure_ascii=False 允许生成的文本直接保留中文;不设置它时,中文可能写成 Unicode 转义形式,解析后仍可以得到相应文字。Python 的 JSON 编码与解码
这里说明的是代码的预期行为,没有进行任何真实日历查询。还要留意,dumps 返回的是字符串,并不会因为名字里有“序列化”,就自动把文件保存到磁盘或把数据发给服务器。
如果要存文件,需要相应的文件写入操作;如果要走网络,需要把文本按约定编码为字节,再交给通信程序。许多 HTTP 库能够帮忙完成一部分转换,但仍然需要知道自己交给库的是字典、字符串,还是已经编码的字节。
这几个层次如果混在一起,就容易出现“重复编码”。
例如,原本准备发送一个会议对象,却先把它变成 JSON 字符串,又让库把这个字符串再次作为 JSON 值编码。接收方可能得到的是“一段字符串”,而不是想要的对象。两种内容都可能符合 JSON 语法,但数据结构不同。
标题里包含双引号、正文里包含换行时,也不宜靠手工拼接一段 JSON。字符串里的特殊字符需要按规则表示,否则可能提前结束字符串或破坏结构。使用合适的序列化工具,可以把这些内容作为字符串正确编码;接收方再按规则解析,才能得到原来的文字。这里的转义是数据表示过程,仍然不是加密。
数据交互应当明确每个环节接收什么。接口要求对象,就提供相应对象表示;工具已经返回解析后的字典,就不必再把它当成原始 JSON 文本解析一次。
反序列化也不意味着把任意 Python 对象原样搬到另一个程序。
JSON 没有内置的日期时间对象、函数、文件连接或自定义类。会议开始时间可以约定为带时区的字符串,但它被解析后通常仍是一段字符串,需要程序另行理解日期含义。
某些 Python 数据结构转换后也会发生变化。例如,元组可以被编码为 JSON 数组,按常见默认规则读回来则成为列表。由此可见,“写出以后又读回来”并不保证恢复所有原始类型,更不保证得到原来那一个内存对象。
跨语言时,还需要注意数值范围。不同语言和解析器对大整数、小数精度的支持可能不同。会议编号如果只是标识,约定为字符串通常更便于避免把它误当成数量;真实数值如何传递,则应结合双方精度要求设计。
JSON 标准允许实现对规模、嵌套深度和数值范围设限;网络交换中的文本采用 UTF-8,有助于不同系统互通。标准 JSON 数字也不包括 NaN 和 Infinity,一些工具的宽松扩展不能直接当作通用约定。JSON 的互操作与编码要求
排版方面,换行和缩进可以让会议记录更好读,紧凑写法则可以减少部分字符。只要变化没有进入字符串内容,许多排版差异不会改变数据结构。对象成员的排列不应承担业务顺序含义;如果确实要表达先后,应该使用数组等适当结构。
同一个对象中的名称也应保持唯一。重复写两个不同的 title,可能使不同解析器保留不同内容,不能把这种重复当成“后面的值更新前面的值”的可靠交互规则。
JSON 还是数据格式,不会因为内容被序列化就自动加密。接收方能否读取数据,取决于传输与访问控制。它也不会自动压缩数据,不能因为去掉缩进,就认为已经完成了专门的压缩处理。
同样,解析成功不表示内容真实或操作已经发生。一个对象写着“草稿已保存”,只能说明它带有这样的值;文件是否存在,仍然需要检查实际结果。
对于外部传来的 JSON,应用应使用相应解析器处理,不要把它当作程序代码执行。还需要限制不合适的数据规模,避免一份过大的记录占用过多资源。这些处理与“内容是不是合法 JSON”有关联,却不能由一个格式名称全部代替。
在 Agent 系统中,模型可能按约定生成工具参数,工具也可能返回 JSON 数据。程序先把这些内容解析成能够处理的结构,再决定怎样使用。模型写出了完整花括号,并不等于参数正确,更不等于已经调用工具。
这就留下了下一篇的问题:既然合法 JSON 可以表达很多不同结构,我们怎样检查会议编号是否必填、时长是否为数字、保存模式是否允许?Schema 与数据校验,就是用来进一步明确这些约定的。
当前学习编号:02.05 JSON 与序列化。
下一学习编号:02.06 Schema 与数据校验。后续还有其他学习编号。
关联分支:输出约定对应 12.06 结构化输出、12.07 JSON Schema 约束;工具交互对应 16.01 Function Calling 与 Tool Calling、16.03 工具输入 Schema、16.07 工具结果结构与错误格式。
跨系统数据约定对应 17.06 MCP 消息、传输与能力协商、21.18 文件格式与模式演进。这些支系会在各自编号中单独展开。