SQLite json_each 与 json_tree:看见了数组,为什么还没有拿到里面的元素

前天 3阅读

把嵌套配置交给 json_each,结果里出现了 panel 和 sizes 两行,却没有 theme 或每个尺寸。json_each 在这里展开的是根对象的直接孩子;一个孩子是对象、另一个孩子是数组,它不会自动把它们里面的内容也铺平成明细。

需要递归检查全部字段时,可以使用 json_tree,再明确选择容器还是叶子。下面通过 Python 连接内存 SQLite,分别打印顶层条目和叶子路径。保存为 demo.py,用 python demo.py 运行;JSON 是代码内固定数据,要求 SQLite 构建包含 JSON 支持。

SQLite json_each 与 json_tree:看见了数组,为什么还没有拿到里面的元素

AI生成概念插图:顶层节点与更深的叶子属于不同遍历范围,放大镜表示递归检查;不是查询工具截图。

import json
import sqlite3

document = json.dumps({
    "panel": {"theme": "blue", "note": None},
    "sizes": [2, 4],
})
db = sqlite3.connect(":memory:")
try:
    top = db.execute(
        "SELECT key, type FROM json_each(?) ORDER BY key", (document,)
    ).fetchall()
    assert top == [("panel", "object"), ("sizes", "array")]
    print("top:", top)

    leaves = db.execute("""
        SELECT fullkey, type, atom
        FROM json_tree(?)
        WHERE type NOT IN ('object', 'array')
        ORDER BY fullkey
    """, (document,)).fetchall()
    assert leaves == [
        ("$.panel.note", "null", None),
        ("$.panel.theme", "text", "blue"),
        ("$.sizes[0]", "integer", 2),
        ("$.sizes[1]", "integer", 4),
    ]
    print("leaves:", leaves)

    nonnull = db.execute(
        "SELECT COUNT(*) FROM json_tree(?) WHERE atom IS NOT NULL",
        (document,),
    ).fetchone()[0]
    assert nonnull == 3 and len(leaves) == 4
    print("all leaves:", len(leaves), "nonnull atoms:", nonnull)

    sizes = db.execute(
        "SELECT key, value FROM json_each(?, '$.sizes') ORDER BY key",
        (document,),
    ).fetchall()
    assert sizes == [(0, 2), (1, 4)]
    print("sizes:", sizes)
finally:
    db.close()

单层与递归应该按问题来选

top 输出两个顶层键及它们的类型。如果目标只是列出有哪些一级配置区块,这正是需要的结果;如果目标是查找任意深度的某个字段,才需要递归视角。json_tree 的结果包含容器本身,因此代码用 type 排除 object 与 array,留下四个叶子。

末尾的 json_each 增加路径 $.sizes,把展开起点移到这个数组,于是得到索引零与一及其数值。已知目标区块时,从指定路径开始能让查询含义更清楚。顶层值若本身是原始值,表值函数也有相应单行行为,不能把“始终返回多个孩子”当成所有输入的保证。

过滤叶子时,要决定是否保留空值

note 明确存在且值为 JSON null,因此叶子清单保留它,Python 中对应 None。atom IS NOT NULL 只留下三个非空原始值,把 note 一并排除了。对只搜索实际值的任务,这个筛选可能合适;对配置字段盘点,它会把“存在但为空”误变成“没有这项”。

fullkey 保存从根开始的完整路径,使不同容器内的同名字段仍能分辨。单独保存 key 往往不够:两个对象可以各有 theme,两个数组也都可以有索引零。代码给结果加 ORDER BY,保证演示稳定;不要把内部 id 或偶然输出顺序当作跨版本不变的业务编号。

value 对对象和数组会提供其 JSON 表示,而 atom 更适合读取原始叶子值。取哪一列应对应下游预期的类型,不能把一个表示数组的文本直接当成已经拆好的多行。布尔、数字、文本和空值也应保留 type,避免序列化后丢掉含义。

实际数据量大时,应先限制要检查的记录和路径,再展开所需部分。本例只展示遍历语义,没有评估大文档性能。遇到格式错误,应先核查输入是否是有效 JSON 和运行库是否具备该功能;不要用吞掉异常后返回空清单的方式掩盖解析失败。

资料核对日期:2026年10月2日。代码在本地 Python 3.12.14、SQLite 3.53.1 实际运行并通过断言;结果只对应文中给定输入。

参考资料

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