Python开发Discord机器人时无法向JSON文件追加保存触发词数据
问题现象
开发支持用户保存自定义聊天触发词的Discord机器人时,存储环节出现异常:先后执行!learn test1 pass、!learn test2 pass两条指令后,JSON文件仅保存1条触发词记录。
当前异常的JSON文件内容:
{ "wordlist": { "word": "test1", "response": "('pass',)", "owner": "b4psa3" } }
对应功能原实现代码:
@client.command() async def learn(ctx, wordreceived, *wordresponse): try: with open("wordlist.p", "rb") as file: what_word_received = pickle.load(file) except FileNotFoundError: with open("wordlist.p", "wb") as file: what_word_received = list() pickle.dump(what_word_received, file) if wordreceived not in what_word_received: what_word_received.append(wordreceived) with open("wordlist.p", "wb") as file: pickle.dump(what_word_received, file) file_path = "./words.json" if os.path.isfile(file_path) == True: with open(file_path, "r", encoding='utf-8') as outfile: data = json.load(outfile) data['wordlist'].append({ "word": f"{wordreceived}", "response": f"{wordresponse}", "owner": f"{ctx.message.author.name}" }) with open(file_path, 'a', encoding='utf-8') as outfile: json.dump(data, outfile, indent=2, ensure_ascii=False) else: data = {} data['wordlist'] = [] data['wordlist'] = { "word": f"{wordreceived}", "response": f"{wordresponse}", "owner": f"{ctx.message.author.name}" } with open(file_path, 'w', encoding='utf-8') as outfile: json.dump(data, outfile, indent=4, ensure_ascii=False) else: print(what_word_received) await ctx.send("This word is already exist")
故障原因
代码里存在3个直接导致存储异常的问题:
- 首次初始化JSON结构错误:先给
data['wordlist']赋值为空列表,紧接着直接将其覆盖为单个字典对象,导致wordlist不是预期的可追加多条记录的列表结构 - JSON写入模式错误:文件已存在时使用
'a'追加模式写入,JSON是结构化文本,直接在文件末尾追加新内容会生成非法格式的JSON文件,无法被正常解析 - 逻辑顺序不合理:重复校验通过后才初始化/读取JSON数据,一旦JSON读写报错,pickle存储的索引和JSON实际存储内容会不一致,后续重复校验完全失效
修复代码
import os import json import pickle @client.command() async def learn(ctx, wordreceived, *wordresponse): pickle_path = "wordlist.p" json_path = "./words.json" # 加载已存触发词索引 try: with open(pickle_path, "rb") as f: word_index = pickle.load(f) except FileNotFoundError: word_index = [] # 先做重复校验 if wordreceived in word_index: print(word_index) await ctx.send("This word is already exist") return # 更新pickle索引 word_index.append(wordreceived) with open(pickle_path, "wb") as f: pickle.dump(word_index, f) # 读取已有JSON数据,兼容旧错误结构 if os.path.exists(json_path): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) # 把之前错误存成单字典的结构转成列表 if isinstance(data.get("wordlist"), dict): data["wordlist"] = [data["wordlist"]] else: data = {"wordlist": []} # 追加新触发词记录 data["wordlist"].append({ "word": wordreceived, "response": wordresponse, "owner": ctx.message.author.name }) # 全量覆盖写入JSON,禁止用追加模式写结构化JSON with open(json_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=4, ensure_ascii=False)
修复说明
- 修正初始化逻辑:
wordlist固定为列表类型,不再错误覆盖为单个字典 - 统一JSON写入方式:所有场景都用
'w'模式全量覆盖写入,避免追加模式生成非法JSON - 增加旧数据兼容:之前生成的错误结构的JSON文件不需要手动删除,代码会自动转换为正确的列表结构
- 调整逻辑顺序:先做重复校验再执行写入操作,减少无效文件IO,避免两个存储文件数据不一致
内容的提问来源于stack exchange,提问作者b4psa3
相关产品推荐
相关产品推荐

