如何用discord.py编写等级查询命令?JSONDecodeError报错如何修复?
问题根因
你的报错json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)本质是读取users.json时,文件为空或者内容不符合JSON格式,导致解析失败。主要触发原因有三个:
on_message事件中bot.process_commands位置错误:该方法被嵌套在非机器人用户消息的判断逻辑、以及JSON写入的缩进块内,只有普通用户发消息且完成经验写入后才会触发命令解析;且w模式打开JSON文件时会先清空内容,写入完成前如果触发level命令读文件,就会读到空内容。- 初始化
users.json的逻辑存在漏洞:原初始化代码的文件句柄没有规范关闭,特殊场景下会导致文件内容写入异常,生成空JSON文件。 - 缺少异常处理:读写JSON的逻辑没有兼容文件损坏/内容为空的场景,一旦出现异常就直接抛出错误。
修复方案
1. 修正on_message事件逻辑
调整bot.process_commands的位置到if判断外,确保所有消息都能正常解析命令,同时增加JSON读取异常兼容:
@bot.event async def on_message(message): # 仅处理普通用户的经验更新逻辑 if not message.author.bot: # 兼容JSON损坏的场景,读失败默认初始化为空字典 try: with open('users.json', 'r') as f: users = json.load(f) except json.decoder.JSONDecodeError: users = {} await add_experience(users, message.author) await level_up(users, message.author, message) with open('users.json', 'w') as f: json.dump(users, f, indent=4) # 加indent方便手动排查JSON结构 # 所有消息都要走命令解析,和if判断同级,不要嵌套在内部 await bot.process_commands(message)
2. 替换JSON文件初始化逻辑
删除原有的混乱初始化代码,替换成更安全的写法,避免文件操作异常:
import os # 不存在文件或者文件为空时,初始化写入空JSON对象 if not os.path.exists("users.json") or os.stat("users.json").st_size == 0: with open("users.json", "w") as f: json.dump({}, f) # 读取初始用户数据 with open('users.json', 'r') as f: users = json.load(f)
3. 优化level命令逻辑
增加异常捕获和用户不存在的兼容场景,避免报错:
@bot.command() async def level(ctx, member: discord.Member = None): target = member if member else ctx.author # 捕获JSON读取异常 try: with open('users.json', 'r') as f: users = json.load(f) except json.decoder.JSONDecodeError: await ctx.send("等级数据读取失败,请稍后重试") return # 兼容用户没有发送过消息、没有数据的场景 if str(target.id) not in users: tip = f"{target.display_name} 还没有等级数据,先发几条消息获取经验吧~" await ctx.send(tip) return lvl = users[str(target.id)]['level'] if member: await ctx.send(f"{target.display_name} 当前等级为 {lvl}!") else: await ctx.send(f"你当前的等级为 {lvl}!")
可选进阶优化(高并发场景)
如果服务器用户量较大,频繁读写JSON容易出现写入中途被读取的问题,可以用原子写入的方式避免:
import shutil # 把原来的写JSON逻辑替换为以下内容 with open('users.tmp', 'w') as f: json.dump(users, f, indent=4) # 写完临时文件后直接替换原文件,不会出现中间读取到空内容的问题 shutil.move('users.tmp', 'users.json')
内容的提问来源于stack exchange,提问作者Just Otartist
相关产品推荐
相关产品推荐

