第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.etree.ElementTree 核心操作 序列化 / 反序列化 常见格式错误 常见错误类型 • 缺少引号(JSON键名未加双引号) • 多余逗号(JSON最后一个元素后加逗号) • 标签未闭合(XML缺少闭合标签) • 属性值未加引号(XML属性值缺少引号) • 编码问题(中文乱码)

这张图把本章的知识点串起来了。你从数据格式入手,选择JSON或XML,然后用Python对应的库进行序列化或反序列化操作。过程中,要时刻警惕那些常见格式错误。说白了,接口联调就是「格式对、数据对、流程对」这三件事。

好了,这一章的内容就到这里。JSON和XML的用法,你多写几次就熟了。遇到问题别慌,对照表格排查,基本都能解决。

可可精品资料库