0

0

Python 中处理嵌套 JSON 字符串字段的双重编码与解码策略

霞舞

霞舞

发布时间:2025-11-06 14:38:01

|

973人浏览过

|

来源于php中文网

原创

Python 中处理嵌套 JSON 字符串字段的双重编码与解码策略

本文探讨了在 python 中对包含已编码 json 字符串的字典进行序列化时遇到的双重编码问题。当内部 json 字符串作为外部字典的一个字段值时,`json.dumps` 会对其进行转义。文章阐明了这种行为是符合预期的,并提供了生产者和消费者双方的正确处理策略,强调消费者需要进行多阶段解码以恢复原始数据。

当在 Python 中处理 JSON 数据时,一个常见的场景是将一个已编码为 JSON 字符串的数据作为另一个 JSON 结构中的字段值。例如,将一个复杂的 JSON 消息封装到一个更简单的信封(envelope)结构中,并通过消息队列发布。然而,直接将已编码的 JSON 字符串作为字段值再次进行 json.dumps 操作时,往往会导致内部引号被双重转义,使得消费者难以直接解析。本文将深入探讨这一现象,并提供一套清晰的生产者与消费者处理策略。

场景描述与问题复现

假设我们有一个复杂的 Python 字典 message,需要先根据 Avro Schema 将其序列化为 JSON 字符串 message_str。

import json
from datetime import datetime
from io import StringIO
import fastavro

# 原始数据字典
message = {
    "name": "any",
    "ingestion_ts": datetime.utcnow(),
    "values": {
        "amount": 5,
        "countries": ["se", "nl"],
        "source": {
            "name": "web",
            "url": "whatever"
        }
    }
}

# 示例 Avro Schema,用于序列化原始消息
avro_schema = {
  "type": "record",
  "name": "MyRecord",
  "fields": [
    {"name": "name", "type": "string"},
    {"name": "ingestion_ts", "type": {"type": "long", "logicalType": "timestamp-micros"}},
    {"name": "values", "type": {
      "type": "record",
      "name": "Values",
      "fields": [
        {"name": "amount", "type": "int"},
        {"name": "countries", "type": {"type": "array", "items": "string"}},
        {"name": "source", "type": {
          "type": "record",
          "name": "Source",
          "fields": [
            {"name": "name", "type": "string"},
            {"name": "url", "type": "string"}
          ]
        }}
      ]
    }}
  ]
}

fo = StringIO()
fastavro.json_writer(fo, avro_schema, [message])
message_str = fo.getvalue()
print(f"原始 JSON 字符串:
{message_str}
")
# 示例输出: '{"name": "any", "ingestion_ts": 1703192665965373, "values": {"amount": 5, "countries": ["se", "nl"], "source": {"name": "web", "url": "whatever"}}}'

现在,我们需要将这个 message_str 封装到一个新的字典 wrap 中,作为 payload 字段的值,并按照特定的 Schema 结构(其中 payload 被定义为 string 类型)发布。

# 外部包装字典
wrap = {
    "sys": "my_system",
    "op": "c",
    "payload": message_str
}

# 再次进行 JSON 编码
wrap_str = json.dumps(wrap)
print(f"双重编码后的 JSON 字符串:
{wrap_str}
")
# 示例输出: '{"sys": "my_system", "op": "c", "payload": "{\"name\": \"any\", \"ingestion_ts\": 1703192665965373, \"values\": {\"amount\": 5, \"countries\": [\"se\", \"nl\"], \"source\": {\"name\": \"web\", \"url\": \"whatever\"}}}"}'

从输出中可以看出,payload 字段的值 message_str 中的双引号被 进行了转义。这使得 wrap_str 看起来不直观,并可能导致消费者在尝试直接解析时遇到问题。

立即学习Python免费学习笔记(深入)”;

理解双重转义的本质

JSON 规范规定,当一个字符串作为 JSON 对象的值时,其内部的特殊字符(如双引号 "、反斜杠 等)必须被转义。在上述例子中,message_str 本身是一个合法的 JSON 字符串。当它被赋值给 wrap 字典的 payload 键时,payload 的值在 Python 层面是一个普通的字符串。随后,json.dumps(wrap) 会将这个字符串作为 JSON 的一个字段值进行处理。根据 JSON 规范,为了确保最终的 JSON 字符串是有效的,它会将其中的所有双引号进行转义,从而产生了 " 的形式。这并非错误,而是 json.dumps 遵循 JSON 规范的正确行为。

AIBox 一站式AI创作平台
AIBox 一站式AI创作平台

AIBox365一站式AI创作平台,支持ChatGPT、GPT4、Claue3、Gemini、Midjourney等国内外大模型

下载

正确的消费者解码策略

问题的关键不在于生产者如何避免双重转义(因为在这种场景下,根据 payload 字段被定义为 string 的 Schema,生产者当前的编码方式是符合规范的),而在于消费者如何正确地处理这种嵌套的 JSON 字符串。消费者需要执行多阶段解码:

  1. 解码外部 JSON 结构: 首先,将接收到的 wrap_str 解码为 Python 字典对象。
  2. 解码内部 JSON 字符串: 从解码后的字典中提取 payload 字段的值,该值此时是一个包含原始 JSON 数据的字符串。然后,对这个字符串再次进行 JSON 解码(或 Avro JSON 解码)。

以下是消费者端解码的示例代码:

# 假设消费者接收到 wrap_str
received_wrap_str = wrap_str # 模拟接收到的数据

# 1. 解码外部 JSON 结构
wrapped_object = json.loads(received_wrap_str)
print(f"解码外部 JSON 后:
{wrapped_object}
")
# 示例输出: {'sys': 'my_system', 'op': 'c', 'payload': '{"name": "any", "ingestion_ts": 1703192665965373, "values": {"amount": 5, "countries": ["se", "nl"], "source": {"name": "web", "url": "whatever"}}}'}

# 2. 从 payload 字段中提取内部 JSON 字符串
payload_json_str = wrapped_object["payload"]
print(f"提取的 payload 字符串:
{payload_json_str}
")

# 3. 对内部 JSON 字符串进行解码(此处使用 fastavro.json_reader)
# 注意:需要提供原始的 Avro Schema
# 重新创建 StringIO 对象以供 fastavro.json_reader 使用
payload_stream = StringIO(payload_json_str)
decoded_payload_records = []
for record in fastavro.json_reader(payload_stream, avro_schema):
    decoded_payload_records.append(record)
    print(f"最终解码的原始消息:
{record}
")
# 示例输出: {'name': 'any', 'ingestion_ts': 1703192665965373, 'values': {'amount': 5, 'countries': ['se', 'nl'], 'source': {'name': 'web', 'url': 'whatever'}}}

通过上述两步解码过程,消费者能够完全恢复原始的 message 数据,避免了因双重转义而导致的解析错误。

注意事项与最佳实践

  • 明确 Schema 定义: 在设计数据传输协议时,务必明确每个字段的类型。如果 payload 字段被定义为 string,那么生产者将其内容编码为字符串是正确的,消费者也应预期它是一个需要二次解析的字符串。如果 payload 被定义为一个复杂的 JSON 对象(例如 type: "record" 或 type: "map"),那么生产者就不应该将其预先编码为字符串,而应该直接传递 Python 字典或对象。
  • 沟通与文档: 生产者和消费者之间必须就数据序列化和反序列化的层级达成一致,并通过清晰的文档进行说明。这有助于避免因误解数据结构而导致的解析失败。
  • 性能考量: 多阶段解码会带来额外的处理开销。对于对性能极度敏感的场景,可以考虑是否能将嵌套的 JSON 结构扁平化,或者重新设计 Schema 以避免这种多层封装。然而,在许多消息队列和事件驱动架构中,这种信封模式是常见且可接受的。

总结

当一个字典字段的值本身是一个已编码的 JSON 字符串时,对其进行再次 JSON 编码会导致内部引号被转义。这并非编码错误,而是 JSON 规范的正常行为。解决此问题的关键在于消费者端采用多阶段解码策略:首先解码外部 JSON 结构,然后将 payload 字段提取出的字符串再次解码。理解数据 Schema 的定义和生产者与消费者之间的数据契约是成功处理此类问题的核心。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1051

2023.08.02

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

761

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1569

2023.10.24

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号