SQLite 接收JSON5配置:json函数能读懂,为什么json_valid仍然返回零

昨天 2阅读

配置为了便于手写,使用单引号、尾随逗号和未加引号的字段名。SQLite的json函数能够转换它,单参数json_valid却报告不合法,表面看像两个函数互相矛盾。实际上,一个入口可以理解扩展语法,另一个检查默认仍坚持标准JSON规则;验证合同要通过参数明确表达。

本例在Linux、CPython 3.12.14与SQLite 3.53.1实跑,使用Python标准库作为SQL实验入口。数据库仅存在于内存中,连接在finally里关闭。程序不读取或修改磁盘数据库,不涉及真实业务数据。 JSON5读取能力自3.42.0加入,本文还使用json_valid的第二参数,该参数自3.45.0加入,因此示例要求至少具备这一版本的能力;本次环境已实际验证支持。

SQLite 接收JSON5配置:json函数能读懂,为什么json_valid仍然返回零

AI生成的概念示意图:一张带自由批注和不规则边缘的配置纸经过整理窗口,输出边界整齐的标准卡片;不是真实软件界面或运行截图。

完整程序与实际输出

保存为demo.py,执行python3 demo.py。代码与输出分列如下。

import json
import sqlite3
con = sqlite3.connect(':memory:')
try:
    raw = "{mode:'fast', retries:0x10, tags:['a',],}"
    strict, flexible = con.execute('SELECT json_valid(?), json_valid(?,2)', (raw, raw)).fetchone()
    normalized = con.execute('SELECT json(?)', (raw,)).fetchone()[0]
    parsed = json.loads(normalized)
    assert (strict, flexible) == (0, 1)
    assert parsed == {'mode': 'fast', 'retries': 16, 'tags': ['a']}
    print('strict/flexible:', strict, flexible)
    print('normalized:', normalized)
    print('strict after normalization:', con.execute('SELECT json_valid(?)', (normalized,)).fetchone()[0])
    print('application retries:', parsed['retries'])
    malformed = '{mode:}'
    print('malformed flexible:', con.execute('SELECT json_valid(?,2)', (malformed,)).fetchone()[0])
    print('scalar valid/root type:', con.execute("SELECT json_valid('42'), json_type('42')").fetchone())
finally:
    con.close()

本次实际标准输出:

strict/flexible: 0 1
normalized: {"mode":"fast","retries":16,"tags":["a"]}
strict after normalization: 1
application retries: 16
malformed flexible: 0
scalar valid/root type: (1, 'integer')

同一输入接受两套明确检查

raw包含未引用的键、单引号字符串、十六进制数以及对象和数组的尾随逗号。第一行strict/flexible为零和一:单参数检查不接受这种标准之外的文本,标志二则允许SQLite所支持的JSON5扩展。两者检查目标不同,所以结果不同是预期行为。

不要看到失败后临时轮流尝试所有标志,只求得到一。应用应该在入口文档里说明接受严格JSON还是扩展配置,并始终采用同一规则。若对外接口承诺只接收JSON,宽松转换可能让测试覆盖不到的写法进入系统,增加跨服务兼容问题。

转换结果再交给下游验证

json函数输出双引号键名与字符串,去掉尾随逗号,并把十六进制十六写成十进制16。程序随后用Python的json.loads读取结果,断言结构与预期字典相等。strict after normalization为一,说明这次输出满足严格JSON语法检查。

这种流程适合允许人工编写扩展配置、但下游只接收标准JSON的受控转换环节。它会改变原文字节和排版,因此若还需要保留批注或原始文本,必须另外保存来源。本文使用的样本没有注释保真需求,也不把转换结果用于原文签名校验。

规范JSON文本并非通用签名规范化

文档中的标准输出含义是生成符合JSON定义的文本,不应据此认定它实现了某种密码学规范化协议。字段顺序、重复键处理、数字表示等要求若影响缓存键或签名,应单独采用明确定义的协议和测试。这里只比较解码后的结构,不比较某种跨实现唯一字节形式。

样本的十六进制数是小整数,能被本次下游完整表示;这不证明任意大数都可无损穿过所有JSON消费者。实际配置应限制数值范围并核对下游类型能力,也应对非有限数值等扩展另订拒绝或转换策略,而不是沿用一个宽松标志包办。

语法通过以后仍要检查结构

malformed示例缺少字段值,即使启用扩展仍返回零。另一方面,字符串42通过严格语法检查,根类型却是integer,不是对象。这说明json_valid不负责保证配置包含mode、retries或tags,更不检查它们的业务含义。

应用可先限制输入大小,按约定验证语法,再规范化并检查根类型、必填字段、允许枚举、重试范围和标签元素类型。不同SQLite版本及构建选项可能支持不同JSON能力,应在启动检查或兼容测试里确认实际函数。本例未涉及JSONB,也未向磁盘持久化配置。

参考资料与验证记录

官方资料核验于2026年10月3日。本次完整程序退出码为0,标准错误为空。


文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。