
Daft 中 JSON 与嵌套数据处理完全指南jq 过滤、字段提取与 unnest 展开【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/DaftDaft 为 JSON 字符串与嵌套Struct/List数据类型提供了完整的处理链路既能通过.jq()表达式 / SQL 的json_query函数以 JQ 风格过滤器在字符串层面直接提取 JSON 值也能在列式数据层面用[]操作符逐层访问嵌套字段、用.unnest()/*通配符将 Struct 整体展开为独立列。本文以 docs/modalities/json.md 为主体结合仓库源码与测试用例从 API 用法、底层执行器、空值语义到真实数据集实战系统讲解在 Daft 中解析、查询和重构嵌套数据的能力。JSON 字符串的 JQ 式过滤.jq()表达式当数据以 JSON 字符串形式存放在某一列中时例如 API 响应体、日志行、WARC 头部Daft 提供.jq()方法直接对字符串应用 JQ 风格的过滤器返回结果为字符串。以下示例从 JSON 对象中提取字段a Pythonpython df daft.from_pydict({ json: [ {a: 1, b: 2}, {a: 3, b: 4}, ], }) df df.with_column(a, df[json].jq(.a)) df.collect() ⚙️ SQLpython df daft.from_pydict({ json: [ {a: 1, b: 2}, {a: 3, b: 4}, ], }) df daft.sql( SELECT json, json_query(json, .a) AS a FROM df ) df.collect()╭──────────────────┬──────╮ │ json ┆ a │ │ --- ┆ --- │ │ Utf8 ┆ Utf8 │ ╞══════════════════╪══════╡ │ {a: 1, b: 2} ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤ │ {a: 3, b: 4} ┆ 3 │ ╰──────────────────┴──────╯ (Showing first 2 of 2 rows)可以看到Python API 与 SQL 语法在语义上完全等价前者通过df[json].jq(.a)后者通过json_query(json, .a)两条路径共享同一套过滤器语义。底层执行器jaqDaft 以jaq一个用 Rust 实现的高性能 jq 克隆作为 jq 过滤器的底层执行引擎因此官方 jq 手册中定义的过滤器语法在 Daft 中均可使用例如字段选择.a、嵌套路径.a.b.c、数组索引.a[0]、管道与内置函数等。过滤器作用于每一行字符串结果以Utf8字符串返回——注意返回的是 JSON 序列化后的文本如数字1、字符串ABC、布尔true而非反序列化后的原生类型。调用链与源码实现从源码可以清晰看到这条调用链表达式层面的入口位于 daft/expressions/expressions.py#L1490-L1498Expression.jq(filter)将调用转发给daft.functions.jq函数实现位于 daft/functions/str.py#L56-L67通过Expression._call_builtin_scalar_fn(jq, ...)下沉到 Rust 内核执行属于内置标量函数因此可以无缝参与select、with_column、filter等任意表达式组合SQL 侧的json_query(json, .a)在 tests/sql/test_nested_access.py#L18-L31 中有与daft.col(json).jq(.b.c)的等价性测试二者可互换使用。常用过滤器模式与空值语义仓库中的 tests/functions/json/test_jq.py 给出了大量可复用的过滤器模式可直接迁移到业务代码中过滤器用途示例来源.a/.b/.c提取顶层字段布尔、数字、字符串test_jq.py#L16-L20.a.b.c多层嵌套路径提取test_jq.py#L42-L49.a.b[0]对象内数组按下标取值test_jq.py#L52-L60.a[0].b/.a[0].b[0]数组元素再深入嵌套test_jq.py#L62-L83.a[].b遍历数组元素逐个提取test_jq.py#L90-L95.a.b?错误抑制运算符路径缺失时返回空串/null而非报错test_jq.py#L100-L120.users[] \| {name: .name, avg_score: ...}管道 对象构造 内置函数组合test_jq.py#L150空值语义是 jq 在列式引擎中的一个关键细节当输入字符串本身为None缺失时jq 结果保持None而当 JSON 字符串中存在显式null字段时jq 返回字符串null。该行为由 test_jq.py#L24-L38 中的test_jq_with_nulls测试固化验证例如输入None时.a结果为None而输入{a: null}时结果为字符串null。在真实日志或 API 数据中这有助于区分字段缺失与字段为 null两种情形。JSON 字符串的反序列化与序列化.jq()返回的是字符串结果若希望将 JSON 解析为结构化的 Daft 类型Struct、List、数值等可使用字符串模块中的deserialize/try_deserialize/serialize函数它们与.jq()同属 daft/functions/str.pydeserialize(expr, formatjson, dtype...)按指定dtype将 JSON 字符串反序列化为结构化类型str.py#L13-L25try_deserialize(...)与deserialize相同但解析失败的行被替换为null而不是抛错str.py#L28-L40serialize(expr, formatjson)反向操作将任意值序列化为 JSON 字符串str.py#L43-L53。这三者同样是下沉到 Rust 内核的内置标量函数deserialize、try_deserialize、serialize可组合进任意表达式。典型的jq 提取 → 反序列化流水线在 tests/functions/json/test_jq.py#L184 中有体现先对字符串应用 jq 过滤器得到 JSON 片段再通过try_deserialize(json, point_t)转为结构化类型并用drop_null()丢弃解析失败的行——这套组合非常适合从脏日志中批量清洗出结构化坐标、指标等数据。嵌套数据的字段提取[]操作符当数据已被读取为 Struct 类型例如 Parquet 中的嵌套列或经deserialize得到的结构化 JSON时可以用[]操作符逐层访问嵌套字段。以下示例直接读取 HuggingFace 上的 nebius/SWE-rebench 数据集的 Parquet 文件只保留meta列import daft from daft import col swe_rebench_metadata daft.read_parquet(hf://datasets/nebius/SWE-rebench/data/*.parquet).select(meta) swe_rebench_metadata.schema()╭─────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ column_name ┆ type │ ╞═════════════╪═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╡ │ meta ┆ Struct[commit_name: Utf8, failed_lite_validators: List[Utf8], has_test_patch: Boolean, is_lite: Boolean, llm_score: Struct[difficulty_score: Int64, issue_text_score: Int64, test_score: Int64], num_modified_files: Int64] │ ╰─────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯meta是一个深度嵌套的 Struct包含标量字段、List[Utf8]列表字段以及内嵌的llm_scoreStruct。使用[]操作符可以链式访问任意深度的字段例如提取llm_score内部的difficulty_scoreswe_rebench_metadata.select(col(meta)[llm_score][difficulty_score]).show()╭──────────────────╮ │ difficulty_score │ │ --- │ │ Int64 │ ╞══════════════════╡ │ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 0 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 0 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 0 │ ╰──────────────────╯ (Showing first 8 rows)[]操作符的关键特性是结果列类型与原字段类型一致difficulty_score提取后保持Int64而非字符串这意味着提取出的嵌套字段可以立刻参与聚合、排序、数值计算无需额外类型转换。这一点与.jq()始终返回Utf8字符串形成明确分工面向字符串的 JSON 用.jq()面向已解析 Struct 的列式访问用[]。整体展开嵌套列.unnest()与*通配符当需要把 Struct 的所有字段一次性展开为独立列时可以使用.unnest()表达式或等价的*通配符swe_rebench_metadata.select(daft.col(meta).unnest()).show() # Alternatively: # swe_rebench_metadata.select(daft.col(meta)[*]).show()╭─────────────┬────────────────────────────────┬────────────────┬─────────┬─────────────────────────────────────────────────────────────────────────────┬────────────────────╮ │ commit_name ┆ failed_lite_validators ┆ has_test_patch ┆ is_lite ┆ llm_score ┆ num_modified_files │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ Utf8 ┆ List[Utf8] ┆ Boolean ┆ Boolean ┆ Struct[difficulty_score: Int64, issue_text_score: Int64, test_score: Int64] ┆ Int64 │ ╞═════════════╪════════════════════════════════╪════════════════╪═════════╪═════════════════════════════════════════════════════════════════════════════╪════════════════════╡ │ head_commit ┆ [has_short_problem_statement,… ┆ true ┆ false ┆ {difficulty_score: 2, ┆ 5 │ │ ┆ ┆ ┆ ┆ issue_t… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ head_commit ┆ [has_many_modified_files, has… ┆ true ┆ false ┆ {difficulty_score: 1, ┆ 5 │ │ ┆ ┆ ┆ ┆ issue_t… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ head_commit ┆ [has_removed_files, has_many_… ┆ true ┆ false ┆ {difficulty_score: 2, ┆ 6 │ │ ┆ ┆ ┆ ┆ issue_t… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ head_commit ┆ [] ┆ true ┆ true ┆ {difficulty_score: 2, ┆ 1 │ │ ┆ ┆ ┆ ┆ issue_t… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ head_commit ┆ [] ┆ true ┆ true ┆ {difficulty_score: 0, ┆ 1 │ │ ┆ ┆ ┆ ┆ issue_t… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ head_commit ┆ [] ┆ true ┆ true ┆ {difficulty_score: 0, ┆ 1 │ │ ┆ ┆ ┆ ┆ issue_t… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ head_commit ┆ [has_hyperlinks, has_issue_re… ┆ true ┆ false ┆ {difficulty_score: 0, ┆ 3 │ │ ┆ ┆ ┆ ┆ issue_t… ┆ │ ╰─────────────┴────────────────────────────────┴────────────────┴─────────┴─────────────────────────────────────────────────────────────────────────────┴────────────────────╯ (Showing first 8 rows)从展开结果可以看到两个重要事实unnest 是浅层展开meta顶层 6 个字段被展开为 6 列其中llm_score仍是嵌套 StructStruct[difficulty_score: Int64, ...]其内部字段并未被递归展开。若需要更深的扁平化可以继续对llm_score链式调用.unnest()或先通过[]逐层访问——展开粒度完全由用户控制。类型全部保留List[Utf8]、Boolean、Int64等原始类型原样保留列表列failed_lite_validators在单元格中显示为[...]截断形式但数据本身完整。.unnest()的源码本质daft/functions/struct.py#L8-L35 揭示了.unnest()的实现本质它的函数体只有一行return expr.get(*)——即通配符字段访问。换言之.unnest()与col(meta)[*]是完全等价的语法糖测试文档中也明确注释了# Alternatively: daft.col(meta)[*]。表达式入口位于 daft/expressions/expressions.py#L206-L214与[]操作符同属 Struct 字段访问体系理解这一点后提取单个字段 vs 展开全部字段就统一为同一个底层原语。SQL 侧的 JSON 查询能力SQL 用户同样拥有完整的嵌套数据访问能力。除了上文展示的json_query(json, .a)对 JSON 字符串应用 jq 过滤器Struct 字段访问、*展开等操作在 SQL 中均有对应语法tests/sql/test_nested_access.py 对 Python 表达式与 SQL 的等价性做了逐一验证。这意味着同一份嵌套数据可以在 Python DataFrame API 与 SQL 之间无缝切换团队内不同技术偏好的成员可以共用同一套数据管线。实战小结如何选择 JSON 处理工具综合以上内容针对不同的数据形态可以按以下原则快速选型场景数据形态推荐 API从 JSON 字符串列中提取若干字段Utf8字符串.jq()/ SQLjson_query结果始终为Utf8将 JSON 字符串解析为结构化类型Utf8字符串deserialize/try_deserialize后者失败置空从已解析 Struct 中提取特定深层字段Structcol(meta)[llm_score][difficulty_score]类型保持将 Struct 全部顶层字段展开为列Struct.unnest()等价于[*]浅层展开、类型保留将结构化值写回 JSON 字符串任意类型serialize(expr, formatjson)推荐组合拳先用.jq()快速抽取字符串片段再用try_deserialize转为结构化类型并丢弃坏行最后用[]或.unnest()在列式层面完成字段提取与展开——这条流水线在 tests/functions/json/test_jq.py#L184 中有完整的端到端示例可覆盖日志清洗、API 响应解析、嵌套元数据分析等绝大多数 JSON 处理需求。更深入的表达式 API 说明可继续查阅 docs/api/expressions.mdJSON 相关的读写与源配置见 docs/modalities/json.md 及 docs/connectors/files.md。【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考