第4章 JSON与XML数据格式:数据序列化与反序列化
说到接口联调,JSON和XML这两个老伙计,你肯定绕不开。我做了这么多年仿真脚本,跟它们打交道的时间,比跟女朋友聊天的时间都多(笑)。
说白了,JSON和XML就是两种「打包方式」。你要把数据从A系统传到B系统,总得有个统一的格式吧?不然你说「张三,25岁,程序员」,对方听成「25,程序员,张三」,那就乱套了。
这一章,我就带你看看这两个格式到底怎么用,以及那些让我踩过坑的常见错误。
4.1 JSON:轻量级的数据交换格式
JSON现在基本是接口联调的主流。为什么?因为它简单、直观、解析快。我刚开始做联调那会儿,XML还是老大,但后来JSON一出来,大家就都「真香」了。
JSON的核心结构就两种:对象(花括号包着)和数组(方括号包着)。对象里是键值对,数组里是一串值。就这么简单。
JSON基本结构示例:
{
"name": "张三",
"age": 25,
"skills": ["Python", "Java", "Go"],
"address": {
"city": "北京",
"district": "海淀"
}
}
你看,这个结构一目了然。name是字符串,age是数字,skills是数组,address是嵌套对象。我在项目中遇到过很多新手,写JSON时忘了给字符串加双引号,或者数字加了引号——嗯,这些都是坑。
4.2 XML:老牌的数据格式
XML虽然现在用得少了,但在一些老系统、银行接口、SOAP协议里还是主力。它的特点是「自描述」——标签本身就说明了数据的含义。
XML的写法比JSON啰嗦,但它的优势在于:支持命名空间、支持属性、支持复杂的文档结构。你想想看,如果数据本身有复杂的层级关系,XML反而更清晰。
XML基本结构示例:
<person>
<name>张三</name>
<age>25</age>
<skills>
<skill>Python</skill>
<skill>Java</skill>
<skill>Go</skill>
</skills>
<address>
<city>北京</city>
<district>海淀</district>
</address>
</person>
我个人习惯是:新项目用JSON,老项目对接用XML。别硬拗,看场景。
4.3 Python的json库使用
Python自带的json库,用起来非常顺手。核心就四个函数:dumps、loads、dump、load。
dumps是把Python对象转成JSON字符串,loads是把JSON字符串转回Python对象。dump和load则是直接读写文件。
json库基本用法:
import json
# Python字典转JSON字符串
data = {"name": "张三", "age": 25}
json_str = json.dumps(data, ensure_ascii=False)
print(json_str) # {"name": "张三", "age": 25}
# JSON字符串转Python字典
parsed = json.loads(json_str)
print(parsed["name"]) # 张三
# 写入文件
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 读取文件
with open("data.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
小技巧:写中文时一定要加 ensure_ascii=False,否则中文会被转成 \uXXXX 的编码。我第一次用json库时就忘了这个参数,结果调试了半天,还以为数据丢了。
4.4 Python的xml库使用
Python处理XML,我推荐用xml.etree.ElementTree。它轻量、够用,不需要装第三方库。
解析XML的核心思路是:把XML文档看成一棵树,每个标签就是一个节点。你通过节点名、属性、文本内容来提取数据。
ElementTree基本用法:
import xml.etree.ElementTree as ET
# 解析XML字符串
xml_str = '''<person>
<name>张三</name>
<age>25</age>
</person>'''
root = ET.fromstring(xml_str)
print(root.find("name").text) # 张三
print(root.find("age").text) # 25
# 解析XML文件
tree = ET.parse("data.xml")
root = tree.getroot()
# 遍历子节点
for child in root:
print(child.tag, child.text)
# 创建XML
new_root = ET.Element("person")
name = ET.SubElement(new_root, "name")
name.text = "李四"
age = ET.SubElement(new_root, "age")
age.text = "30"
tree = ET.ElementTree(new_root)
tree.write("new_data.xml", encoding="utf-8", xml_declaration=True)
注意:XML的标签名是大小写敏感的。我曾经因为把 <Name> 写成了 <name>,结果find了半天找不到数据。这种低级错误,排查起来最浪费时间。
4.5 常见格式错误与避坑指南
做接口联调这么多年,我总结了几类最常见的格式错误。你遇到了,直接对照排查就行。
| 错误类型 | JSON示例 | XML示例 | 说明 |
|---|---|---|---|
| 缺少引号 | {name: "张三"} |
— | 键名必须用双引号 |
| 多余逗号 | {"a":1, "b":2,} |
— | 最后一个元素后面不能有逗号 |
| 标签未闭合 | — | <name>张三 |
每个标签必须有对应的闭合标签 |
| 属性值未加引号 | — | <item id=123> |
属性值必须用双引号或单引号 |
| 编码问题 | 中文乱码 | 中文乱码 | 确保文件编码为UTF-8 |
避坑指南:我曾经在联调一个银行接口时,对方返回的XML里有一个<![CDATA[ ... ]]>段,里面包含了特殊字符。我直接用find去取文本,结果取出来的是空字符串。后来才发现,CDATA段需要用.text属性来获取,但前提是解析器要正确处理。
4.6 序列化与反序列化的核心逻辑
序列化,就是把内存中的对象转成可传输的格式(JSON字符串、XML字符串)。反序列化,就是反过来,把字符串转回对象。
这个流程,说白了就是「打包」和「拆包」。你寄快递,得把东西装进箱子里(序列化),对方收到后拆开箱子(反序列化),才能拿到里面的东西。
在接口联调中,序列化和反序列化是成对出现的。你发请求时要序列化,收响应时要反序列化。如果两边的格式不匹配,那就出问题了。
序列化与反序列化流程:
# 序列化:Python对象 → JSON字符串
data = {"name": "张三", "age": 25}
json_str = json.dumps(data)
# 发送请求...
# 反序列化:JSON字符串 → Python对象
response_str = '{"name": "李四", "age": 30}'
parsed = json.loads(response_str)
print(parsed["name"]) # 李四
嗯,这里要注意:序列化和反序列化的类型映射要一致。比如JSON的数字类型,在Python里会变成int或float。如果你在JSON里写了"age": "25"(字符串),那反序列化后就是字符串,不是数字。这个细节,在接口联调时经常引发bug。
4.7 知识体系总览
我把这一章的核心逻辑画成了图,方便你理解。JSON和XML是两种数据格式,Python的json库和xml库负责序列化与反序列化,而常见格式错误就是你在联调过程中需要避开的坑。
这张图把本章的知识点串起来了。你从数据格式入手,选择JSON或XML,然后用Python对应的库进行序列化或反序列化操作。过程中,要时刻警惕那些常见格式错误。说白了,接口联调就是「格式对、数据对、流程对」这三件事。
好了,这一章的内容就到这里。JSON和XML的用法,你多写几次就熟了。遇到问题别慌,对照表格排查,基本都能解决。
可可精品资料库