解决方案 —— 对于反序列化出现问题,出现多重转义符号的json文件解析
背景
最近年后受到导师推荐来一个做算法的公司实习,公司最近新推出了产品,可以提供稳定模型服务等,我作为小小实习生,为了研究 deepseek 模型的prefix caching的实现,收到的任务就是研究一下提示词的前缀和如果进行前缀缓存命中率
问题描述
遇到的数据经过一点点省略后如下:
{"request_body":"{\\\"model\\\":\\\"deepseek/deepseek_v3\\\",\\\"messages\\\":[{\\\"role\\\":\\\"system\\\",\\\"content\\\":\\\"You ...
问题分析以及尝试过的解决方案
requestbody里的数据,经过了错误的多重转义。
- 试图和ai交互来解决这些问题,结果4o,cloude3,deepseekr1全废(2025.2.11)
有可能是因为在输入的时候,作为string读入或者别的,ai去理解这些带转义符的字符串也会受到阻碍,甚至有些ai在读入这些字符串的时候会查错转义字符的数量
- 自己自作聪明写了类似的解决方案:
def parse_request_body(text):
for i in range(20,0,-1):
pattern = "\\" * i + '"'
text = text.replace(pattern,'"')
pattern = "\\" * i + 'n'
text = text.replace(pattern,'\n')
事后来分析,思路整体上没什么问题,不管是由于几次错误转义导致的问题,都能差不多还原回来,唯一的问题是可能要枚举处理所有特殊情况,比如unicode,比如制表符。
本以为此时找到了一个相对不错的方案,只要没有用户瞎输入类似于\\",或者\n这种子串,应该都不会破坏本来的输入还原回来。
遇到的问题
本以为还不错的解决方案,没想到碰到高手了:
人家本来在message里的较多的转义符,就是不想被翻译成"的,不然反倒会影响json的解析,也就是说本来json里就该有这些//
我还傻乎乎的在这转换,结果出来的都没办法解析。
真正的高手方案
正苦恼,没办法通过简单的替换来解决(其实按照正确的顺序,一层一层的解,是有机会替换回去的),结果看到了一个解决方案
import json
import request_part as reqs
print("Parsing %d requests...", len(reqs.request))
for i in range(len(reqs.request)):
# 解析 JSON 字符串
data = json.loads(reqs.request[i])
# 提取 "messages" 字段的内容
req_body = json.loads(data['request_body'])
而request_part文件,就是把json文件读入后,把整个python list输出出来(list的每一项由于没法被解析为python,都是字符串形式)
问题居然就这样解决了???!!!
居然只是把json存储变成python列表存储,就可以处理转义字符了???
原因分析
十分不服,也很困惑,于是写了如下的代码进行对比:
def extract_messages(data):
content_list = []
req_body = reqs.request
for i in range(1):
data_1 = reqs.request[i]
data_2 = data[i]
print(repr(data_1))
print('--------------------------------')
print(repr(data_2))
print('--------------------------------')
# 比较 data_1 和 data_2是否相同
if data_1 == data_2:
print("data_1 和 data_2_str 是相同的")
else:
print("data_1 和 data_2_str 是不同的")
return content_list
if __name__ == "__main__":
cache_file_path = 'json_data_cache.pkl'
data = read_data_with_cache(cache_file_path)
extract_messages(data)
data_1就是他的数据,data_2是我的数据,经过比较发现,唯一的区别就是,我的是json数据,他的是string。
为什么我是json呢?因为我在别的程序里,使用json.loads读取了json文件,并且存储为了pickle。
我此时对于data_1,可以通过两层加载的方式自动解码:
data_1_outer = json.loads(data_1)
req_body = json.loads(data_1_outer['request_body'])
而为什么我没办法去解码,或者我在存储pickle的文件为什么外层被扒开了而里层还没开呢?
一切的罪魁祸首就是tqdm!
真凶揭晓时刻
这是我读取json并缓存pickle文件的代码:
def read_json_with_cache(json_file_path, cache_file_path):
"""
读取JSON文件并缓存数据。
:param json_file_path: JSON文件的路径
:param cache_file_path: 缓存文件的路径
:return: 解析后的JSON数据
"""
# 如果缓存文件存在,直接加载缓存数据
if os.path.exists(cache_file_path):
with open(cache_file_path, 'rb') as cache_file:
print("从缓存加载数据...")
return pickle.load(cache_file)
# 否则,读取JSON文件并缓存数据
try:
with open(json_file_path, 'r', encoding='utf-8') as json_file:
# 使用tqdm显示进度条
file_size = os.path.getsize(json_file_path)
with tqdm(total=file_size, unit='B', unit_scale=True, desc=json_file_path) as pbar:
# 逐行读取文件并更新进度条
data = []
for line in json_file:
try:
json_object = json.loads(line)
data.append(json_object)
except json.JSONDecodeError as e:
print(f"解析行时发生错误: {e}")
pbar.update(len(line))
# 将数据写入缓存文件
with open(cache_file_path, 'wb') as cache_file:
pickle.dump(data, cache_file)
return data
except FileNotFoundError:
print(f"文件 {json_file_path} 未找到。")
except json.JSONDecodeError as e:
print(f"文件 {json_file_path} 不是有效的JSON格式。错误信息: {e}")
# 打印出文件的前几行以帮助调试
with open(json_file_path, 'r', encoding='utf-8') as json_file:
for i in range(5):
print(json_file.readline().strip())
except Exception as e:
print(f"读取文件时发生错误: {e}")
一切的原因都是,我在这里处理数据的时候,为了能看读取的进度来确定程序是否卡死,我使用了逐行读入,并逐行写入pickle。其中,如果在同行的json就被loads进行了第一次解析和转义,跨行的json被当作了内容字符串,没进行解析。少进行了一次转义
这导致我的pickle文件里面的数据是外层被转义过一次,部分里层比外层少转义一次,但是部分数据转义层数又是正常的。
至此,问题解决。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了