教程:https://www.bilibili.com/video/BV1yjz5BLEoY

代码:https://github.com/shangxiang0907/HeiMa-AI-LLM-RAG-Agent-Dev

云开发平台:https://hzh.sealos.run

  • JSONLoader 参数:

    • file_path: 文件路径
    • jq_schema: jq schema 语法,用于指定提取规则
    • text_content: 抽取的是否是字符串,默认 True(False 时提取为 Python 对象)
    • json_lines: 是否是 JsonLines 文件(每一行都是 JSON 的文件),默认 False
  • text_content总结:就是说如果用text_content=True,解析为字符串,那么使用jq_schema获取的类型必须是字符串,不能为其他类型比如字典、列表

  • 原始的结果类型,完全由 jq_schema + JSON 结构 决定。text_content 决定这个结果能不能当作 page_content

RAG开发-24、JSONLoader

jq介绍(JSONLoader依赖于jq)

在这里插入图片描述

JSONLoader介绍

在这里插入图片描述

→提示词:帮我根据图片内容,在目录@AI_LLM_RAG_Agent_Dev 创建代码29,介绍JSONLoader

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

←AI回复

查看现有代码结构和数据文件,以便创建 JSONLoader 示例代码。

[3 tools called]

创建 JSONLoader 示例代码。先创建所需的 JSON 示例文件,然后编写演示代码。

[1 tool called]

创建 JSON 示例文件,然后编写 JSONLoader 演示代码。

[4 tools called]

检查代码是否有错误:

[1 tool called]

已创建 JSONLoader 示例代码。内容如下:

创建的文件

  1. 29_LangChain_JSONLoader.py - 主演示文件
  2. stu.json - 单个 JSON 对象示例(包含嵌套对象和数组)
  3. stus.json - JSON 数组示例
  4. stu_json_lines.json - JsonLines 格式示例

代码包含的示例

  1. 示例1:jq_schema 基本语法 - 演示从单个 JSON 对象中提取数据

    • . - 提取整个对象
    • .name - 提取字段
    • .hobby[1] - 提取数组元素
    • .other.addr - 提取嵌套字段
  2. 示例2:JSON 数组提取 - 演示从 JSON 数组中提取数据

    • .[] - 遍历数组
    • .[].name - 提取所有对象的 name 字段
  3. 示例3:JsonLines 格式 - 演示加载 JsonLines 格式文件

    • 使用 json_lines=True 参数
  4. 示例4:text_content 参数 - 演示 text_content 参数的区别

    • True:提取为字符串
    • False:提取为 Python 对象
  5. 示例5:lazy_load() 方法 - 演示延迟流式加载

  6. 示例6:复杂 jq_schema - 演示更复杂的提取规则

代码遵循了现有代码风格(参考 28_LangChain_CSVLoader.py),包含中文注释和说明。可直接运行查看效果。

注意:使用 JSONLoader 前需要安装 jq 库:

pip install jq

运行代码

python3 AI_LLM_RAG_Agent_Dev/29_LangChain_JSONLoader.py

结果

================================================================================
LangChain JSONLoader 文档加载器示例
================================================================================

================================================================================
【示例1】jq_schema 基本语法 - 单个 JSON 对象
--------------------------------------------------------------------------------
创建示例 JSON 文件:./stu.json
示例 JSON 文件已创建:./stu.json


. - 提取整个 JSON 对象
--------------------------------------------------------------------------------
成功加载 1 个文档
文档 1:
  page_content: {"name": "\u5468\u6770\u8f6e", "age": 11, "hobby": ["\u5531", "\u8df3", "RAP"], "other": {"addr": "\u6df1\u5733", "tel": "12332112321"}}
  metadata: {'source': '/home/devbox/project/stu.json', 'seq_num': 1}

.name - 提取 name 字段(周杰轮)
--------------------------------------------------------------------------------
成功加载 1 个文档
文档 1:
  page_content: 周杰轮
  metadata: {'source': '/home/devbox/project/stu.json', 'seq_num': 1}

.hobby - 提取 hobby 数组
--------------------------------------------------------------------------------
成功加载 1 个文档
文档 1:
  page_content: ["\u5531", "\u8df3", "RAP"]
  metadata: {'source': '/home/devbox/project/stu.json', 'seq_num': 1}

.hobby[1] - 提取 hobby 数组的第二个元素(跳)
--------------------------------------------------------------------------------
成功加载 1 个文档
文档 1:
  page_content: 跳
  metadata: {'source': '/home/devbox/project/stu.json', 'seq_num': 1}

.other.addr - 提取嵌套对象 other 中的 addr 字段(深圳)
--------------------------------------------------------------------------------
成功加载 1 个文档
文档 1:
  page_content: 深圳
  metadata: {'source': '/home/devbox/project/stu.json', 'seq_num': 1}

================================================================================
【示例2】JSONLoader - JSON 数组提取
--------------------------------------------------------------------------------
创建示例 JSON 数组文件:./stus.json
示例 JSON 数组文件已创建:./stus.json


.[] - 提取数组中的每个对象(得到 3 个字典)
--------------------------------------------------------------------------------
成功加载 3 个文档
文档 1:
  page_content: {"name": "\u5468\u6770\u8f6e", "age": 11, "gender": "\u7537"}
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 1}
文档 2:
  page_content: {"name": "\u8521\u4f9d\u4e34", "age": 12, "gender": "\u5973"}
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 2}
文档 3:
  page_content: {"name": "\u738b\u529b\u9e3f", "age": 11, "gender": "\u7537"}
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 3}

.[].name - 提取数组中所有对象的 name 字段(得到 3 个名字)
--------------------------------------------------------------------------------
成功加载 3 个文档
文档 1:
  page_content: 周杰轮
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 1}
文档 2:
  page_content: 蔡依临
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 2}
文档 3:
  page_content: 王力鸿
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 3}

.[].age - 提取数组中所有对象的 age 字段
--------------------------------------------------------------------------------
成功加载 3 个文档
文档 1:
  page_content: 11
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 1}
文档 2:
  page_content: 12
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 2}
文档 3:
  page_content: 11
  metadata: {'source': '/home/devbox/project/stus.json', 'seq_num': 3}

================================================================================
【示例3】JSONLoader - JsonLines 格式
--------------------------------------------------------------------------------
创建示例 JsonLines 文件:./stu_json_lines.json
JsonLines 格式:每行都是一个独立的 JSON 对象
示例 JsonLines 文件已创建:./stu_json_lines.json

使用 JSONLoader 加载 JsonLines 文件:
--------------------------------------------------------------------------------
成功加载 3 个文档

所有文档内容:

文档 1:
  page_content: {"name": "\u5468\u6770\u8f6e", "age": 11, "gender": "\u7537"}
  metadata: {'source': '/home/devbox/project/stu_json_lines.json', 'seq_num': 1}

文档 2:
  page_content: {"name": "\u8521\u4f9d\u4e34", "age": 12, "gender": "\u5973"}
  metadata: {'source': '/home/devbox/project/stu_json_lines.json', 'seq_num': 2}

文档 3:
  page_content: {"name": "\u738b\u529b\u9e3f", "age": 11, "gender": "\u7537"}
  metadata: {'source': '/home/devbox/project/stu_json_lines.json', 'seq_num': 3}

================================================================================
【示例4】JSONLoader - text_content 参数区别
--------------------------------------------------------------------------------

1. text_content=True - 提取简单字符串值
--------------------------------------------------------------------------------
使用 jq_schema='.name' 提取字符串字段
成功加载 1 个文档
page_content 类型: <class 'str'>
page_content: 周杰轮

2. text_content=False - 提取复杂对象(字典、数组)
--------------------------------------------------------------------------------
使用 jq_schema='.' 提取整个 JSON 对象
成功加载 1 个文档
page_content 类型: <class 'str'>
page_content: {"name": "\u5468\u6770\u8f6e", "age": 11, "hobby": ["\u5531", "\u8df3", "RAP"], "other": {"addr": "\u6df1\u5733", "tel": "12332112321"}}

3. 错误示例:提取字典对象但 text_content=True
--------------------------------------------------------------------------------
使用 jq_schema='.' 提取整个对象,但 text_content=True(会报错)
预期的错误:Expected page_content is string, got <class 'dict'> instead.                     Set `text_content=False` if the desired input for                     `page_content` is not a string
解决方案:当 jq_schema 提取的是字典或数组时,必须设置 text_content=False

================================================================================
【示例5】JSONLoader lazy_load() 方法 - 延迟流式加载
--------------------------------------------------------------------------------
使用 lazy_load() 方法逐个加载文档:
--------------------------------------------------------------------------------
文档 1:
  page_content: {"name": "\u5468\u6770\u8f6e", "age": 11, "gender": "\u7537"}
  metadata: {'source': '/home/devbox/project/stu_json_lines.json', 'seq_num': 1}
文档 2:
  page_content: {"name": "\u8521\u4f9d\u4e34", "age": 12, "gender": "\u5973"}
  metadata: {'source': '/home/devbox/project/stu_json_lines.json', 'seq_num': 2}
文档 3:
  page_content: {"name": "\u738b\u529b\u9e3f", "age": 11, "gender": "\u7537"}
  metadata: {'source': '/home/devbox/project/stu_json_lines.json', 'seq_num': 3}

... (其余文档省略)

================================================================================
【示例6】JSONLoader - 复杂 jq_schema 用法
--------------------------------------------------------------------------------
创建复杂 JSON 文件:./complex_stu.json
复杂 JSON 文件已创建:./complex_stu.json


.[].name - 提取所有对象的 name 字段
--------------------------------------------------------------------------------
成功加载 2 个文档
文档 1:
  page_content: 周杰轮
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 1}
文档 2:
  page_content: 蔡依临
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 2}

.[].hobby - 提取所有对象的 hobby 数组
--------------------------------------------------------------------------------
成功加载 2 个文档
文档 1:
  page_content: ["\u5531", "\u8df3", "RAP"]
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 1}
文档 2:
  page_content: ["\u5531", "\u8df3"]
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 2}

.[].hobby[] - 提取所有对象的所有 hobby 元素(扁平化)
--------------------------------------------------------------------------------
成功加载 5 个文档
文档 1:
  page_content: 唱
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 1}
文档 2:
  page_content: 跳
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 2}
文档 3:
  page_content: RAP
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 3}

... (还有 2 个文档)

.[].other.addr - 提取所有对象的地址
--------------------------------------------------------------------------------
成功加载 2 个文档
文档 1:
  page_content: 深圳
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 1}
文档 2:
  page_content: 北京
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 2}

.[] | {name: .name, addr: .other.addr} - 提取并组合多个字段
--------------------------------------------------------------------------------
成功加载 2 个文档
文档 1:
  page_content: {"name": "\u5468\u6770\u8f6e", "addr": "\u6df1\u5733"}
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 1}
文档 2:
  page_content: {"name": "\u8521\u4f9d\u4e34", "addr": "\u5317\u4eac"}
  metadata: {'source': '/home/devbox/project/complex_stu.json', 'seq_num': 2}

已清理临时文件:./complex_stu.json

================================================================================
演示结束
================================================================================

提示:
- 示例 JSON 文件已创建在当前目录:
  - stu.json(单个对象)
  - stus.json(数组对象)
  - stu_json_lines.json(JsonLines 格式)
- 可以手动编辑这些文件来测试不同的 JSON 格式
- 更多 jq 语法请参考:https://stedolan.github.io/jq/manual/
- 更多文档加载器请参考:https://docs.langchain.com/oss/python/integrations/document_loaders

→问AI:@AI_LLM_RAG_Agent_Dev/29_LangChain_JSONLoader.py:138-139 我发现文件中存储utf-8编码的汉字是正常显示的,但是读取出来打印,变成unicode格式类似\u这种的值了,这是为什么,只需回答不用改代码

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

←AI回复

出现这种情况的原因:

原因

  1. JSON 序列化行为:JSONLoader 内部使用 Python 的 json 模块处理数据。json.dumps() 默认会将非 ASCII 字符(如中文)转义为 \uXXXX 格式,这是 JSON 标准允许的表示方式。

  2. Python 对象的 repr() 显示:当 text_content=False 时,page_content 是字典/列表等 Python 对象。打印这些对象时,Python 会调用 repr(),它也会将非 ASCII 字符显示为 Unicode 转义序列,这是 Python 的默认行为。

  3. 实际数据未改变:文件中的 UTF-8 汉字在内存中仍是正确的 Unicode 字符串;只是打印时被转义显示。

验证方法

可以这样验证实际内容:

# 如果 page_content 是字符串
print(doc.page_content.encode('utf-8'))  # 查看实际字节

# 如果 page_content 是字典
import json
print(json.dumps(doc.page_content, ensure_ascii=False))  # 确保不转义 ASCII

总结:这是显示层面的转义,数据本身是正确的。如果需要避免转义显示,可以在打印时使用 json.dumps(..., ensure_ascii=False) 或直接访问字典的键值。

→问AI:JSONLoader text_content参数string format是什么意思,我不太理解

←AI回复

简短结论text_content 的作用,就是决定 Document.page_content 里放的是“字符串”还是“原始 Python 对象(字典/列表等)”。

具体含义

  • text_content=True(默认,string format)

    • JSONLoader 会把抽取到的结果转成字符串,再放进 Document.page_content
    • 无论你用 jq_schema 拿到的是一个字符串、数字、字典还是数组,最后都会被 str(...) 成一个字符串。
    • 这样做的好处:后面的文本处理(切分、嵌入、向量检索等)都统一处理字符串,更简单。
  • text_content=False

    • JSONLoader 会保持原始结构,按 JSON 解析后的 Python 对象存到 page_content 中:
      • 对象 → dict
      • 数组 → list
      • 数字 → int/float
      • 字符串 → str(本来就是字符串)
    • 适合你想对结构化数据做进一步编程处理,而不仅仅当成纯文本时。

结合你代码中的这两行

loader = JSONLoader(
    file_path=json_file,
    jq_schema=jq_schema,
    text_content=False,
)

这里设置 text_content=False,意思是:按 jq_schema 抽取出来的 JSON 片段,不再转成字符串,而是按原始的 JSON 结构(dict / list 等)放进 Document.page_content,你打印时看到的是 Python 对这些对象的显示形式,而不是“文本格式”的 JSON。

问AI:使用JSONLoader text_content=False相较text_content=True有什么好处呢?

AI回复

一句话概括text_content=False 的好处,是保留原始 JSON 结构(dict/list 等)做“结构化处理”,而不是把一切都压成字符串做“纯文本处理”。

text_content=True 的核心区别

  • text_content=True(默认)

    • 把 jq 抽取到的结果统一转成字符串放到 page_content
    • 更适合:后面要做的是纯文本相关的事情(切分、向量化、语义检索、LLM 问答等)。
  • text_content=False

    • 把 jq 抽取到的结果保留为原始 Python 对象dict / list / int / float / str
    • 更适合:后面要做的是结构化数据处理 / 编程逻辑,比如:
      • 直接访问字段:doc.page_content["name"]doc.page_content["age"] > 18
      • 过滤 / 聚合:求平均年龄、按性别分组等
      • 再组装成别的结构写回数据库 / ES / DataFrame

具体好处

  • 1. 保留类型信息,不丢结构

    • True{"age": 11} 变成 "{'age': 11}" 这样的字符串,你要先解析回对象才能计算。
    • False:直接是 {"age": 11}(dict),可以立刻做数值运算、逻辑判断。
  • 2. 方便写“业务逻辑”

    • 例如只保留年龄 > 18 的学生:
      • True:需要先 eval/json.loads 或正则再取字段。
      • False[d for d in docs if d.page_content["age"] > 18] 一行搞定。
  • 3. 避免多次序列化 / 反序列化

    • True:JSON -> Python 对象 -> 字符串 ->(你再用时)字符串 -> Python 对象。
    • False:JSON -> Python 对象,一次到位,少一次来回转换和出错点。
  • 4. 控制输出格式

    • False 时,你可以自己决定怎么序列化:
      • json.dumps(obj, ensure_ascii=False, indent=2) 保留中文、不转 \uXXXX
    • True 时,内容已经是“字符串表示”,再控制就比较别扭。

什么时候用哪个?

  • 偏“LLM / 文本检索”:大多数时候用 text_content=True,因为后面都是把 page_content 当纯文本喂给模型。
  • 偏“数据分析 / ETL / 规则引擎 / 数值计算”:更推荐 text_content=False,直接拿结构化数据做处理,再按需要自己转成文本。

→问AI:page_content 的类型是怎么决定的?是自动判断的吗?

错误:Expected page_content is string, got <class 'dict'> instead. 

←AI回复

核心结论先说清楚

  • 为什么 page_contentdict
    因为你现在的 jq_schema=".",对这个 stu.json 来说,. 表示“整个根对象”,而根是 { ... },在 Python 里就是 dict,所以 jq 返回的是一个字典,JSONLoader 也就把这个字典放进了 page_content 里。

  • 这个类型是怎么决定的?是自动判断的吗?
    分两层来理解:

    • 1)jq 层决定“原始类型”

      • jq 根据 JSON 结构返回结果:
        • 顶层 { ... } → jq 返回对象 → Python 里就是 dict
        • 顶层 [ ... ] → jq 返回数组 → Python 里就是 list
        • 顶层 "xxx" → jq 返回字符串 → Python 里就是 str
        • 顶层 123 → Python int/float
      • 也就是说:jq_schema + JSON 结构 = 决定“原始结果”的 Python 类型
    • 2)JSONLoader 再根据 text_content 处理这个“原始结果”

      • text_content=True(默认)时,JSONLoader 认为:

        “jq 返回的应该是字符串(或简单标量),我会把它作为 Document.page_content 文本用。”

        • 如果实际拿到的是 str → 正常
        • 如果实际拿到的是 dict / list → 抛错:
          Expected page_content is string, got <class 'dict'> instead. Set text_content=False ...
      • text_content=False 时,JSONLoader 认为:

        “jq 返回的是结构化数据(dict/list 等),我就按原样当 Python 对象保存,不强制转成字符串。”

    所以:它不会“智能自动判断然后帮你改类型”,而是:

    • jq 决定“结果本身是什么类型”;
    • 你用 text_content 告诉 JSONLoader
      • “这个结果我打算当字符串用”(True),
      • 还是“这个结果就当结构化对象用”(False)。
        如果两者不匹配(比如结果是 dict,你却说要当字符串),它就直接报错提醒你。

对应你代码里的这一段具体说明

loader = JSONLoader(
    file_path=json_file,
    jq_schema=".",       # 对 stu.json 来说,返回整个对象 -> dict
    text_content=True,   # 这里声明“我要字符串”
)
  • jq:. → 返回整个 stu.json 对象 → Python dict
  • JSONLoader 看见 text_content=True,期望拿到的是 str,结果拿到 dict → 报你看到的这个错。

如果你想:

  • 拿到 dict
loader = JSONLoader(
    file_path=json_file,
    jq_schema=".",
    text_content=False,  # 告诉它:这是对象,不是字符串
)
  • 还是想要字符串,但内容是整个对象

    • 方式 1:在 jq 里转:

      loader = JSONLoader(
          file_path=json_file,
          jq_schema=". | tostring",
          text_content=True,  # 现在 jq 已经保证是字符串了
      )
      
    • 方式 2:先 text_content=False 拿到 dict,再用 json.dumps(...) 自己转成你想要的字符串格式。

问AI:所以说jq_schema和 JSON 结构 就已经决定了page_content的类型是吗?

AI回复

可以这样理解,但要加一个“小尾巴”:

  • 1)jq_schema 决定“原始结果的 Python 类型”
    jq 根据 jq_schema 在 JSON 里取数据,取出来是什么就是什么:

    • .{...}dict
    • .name"周杰轮"str
    • .hobby[...]list
      所以:原始的结果类型,完全由 jq_schema + JSON 结构 决定。
  • 2)text_content 决定这个结果能不能当作 page_content

    • text_content=True:要求这个“原始结果”是字符串,否则报错。
    • text_content=False:允许 dict/list 等复杂对象,直接当作 page_content 保存。

总结一句:jq_schema 决定“结果本身是什么类型”,text_content 决定“这个类型能不能被当成 page_content 用,以及用什么方式用”。

总结

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐