实现一个流式 Json 解析器,解决既要求结构化数据又需要实时流式输出的一种思路

2025 年 12 月 16 日
 wantDoraemon

为什么需要流式 JSON 解析?

AI 模型的输出通常是逐字生成的流式数据,尤其是在实时对话交互的场景中,用户希望能即时看到 AI 输出内容的过程。而另一方面,我们在很多场景下需要 AI 模型输出结构化的内容,方便我们后续处理和展示。我们可以通过 markdown 的标题格式来组织 AI 的输出,但是 markdown 的无法做到强约束。如果需要保证强结构化,那么我们一般会采用 JSON 格式,对此很多模型都有强制约束输出 Json 的参数。 这其中有两大问题:

  1. 高延迟 :如果使用 Json 格式,传统的 Json.Parse 方法必须等待整个字符串接收完毕才能解析,这会导致高延迟问题。用户无法实时看到 AI 的思考过程,体验大打折扣。
  2. 交互不稳定 :如果使用 Markdown 格式,AI 输出可能不符合预期,导致前端展示异常。

因此,我们需要一种能够边接收数据边解析的方案,确保用户实时看到 AI 的输出,同时保证解析的健壮性。


场景

例如我们在理解用户问题这个场景时,既想要结构化的数据,又想要实时的将结果反馈到前端

设计目标

为了解决上述问题,我们设计了一款流式 JSON 解析器,目标包括:

  1. 实时性:支持逐字符解析,边接收边触发回调。
  2. 路径订阅:允许用户按需订阅 JSON 中的特定路径(如 $.nodes[*].title),减少无效数据处理。
  3. 增量输出:针对字符串值,仅发送新增部分,避免重复传递完整值。
  4. 健壮性:即使 JSON 格式不完整或后续部分有错误,已解析的数据也能正常使用。

核心实现

我们的解析器基于手写的有限状态机( FSM ),逐字符处理流式数据。以下是实现的关键组件和流程:

1. StreamingJsonParser (流式解析器)

状态机的核心逻辑如下:

2. SimplePathMatcher (路径匹配器)


增量与实时模式

解析器支持两种模式:

  1. 实时模式( realtime=true ):在值尚未最终确定时,依据当前缓冲区内容触发回调,适合逐字生成的场景。
  2. 增量模式( incremental=true ):针对字符串值,仅发送新增部分,避免重复传递完整值。

以下是增量解析的示例:

matcher := utils.NewSimplePathMatcher()
matcher.On("$.choices[0].delta", func(value interface{}, path []interface{}) {
    fmt.Printf("path=%v, value=%v\n", path, value)
})

parser := utils.NewStreamingJsonParser(matcher, true, true)
_ = parser.Write("{\"choices\":[{\"delta\":\"")
_ = parser.Write("Hel") // 增量触发回调:"Hel"
_ = parser.Write("lo\"}]}\n") // 增量触发回调:"lo",结束后不再发送整串
_ = parser.End()

性能与内存优化


常见问题与改进方向

  1. 字符串转义:支持常见转义 \n, \t, \r, \\, \", \/, \b, \f;当前未支持 \uXXXX Unicode 转义序列,可按需扩展。
  2. 路径匹配:目前为精确匹配,可考虑支持前缀匹配或更完整的 JsonPath 语法。
  3. 增量支持:目前仅支持字符串值的增量输出,未来可扩展至对象和数组。

文档&代码实现

代码使用 Golang 实现,如果需要使用其他语言,可以让 AI 翻译一下即可。

通过自研流式 JSON 解析器,成功解决了 AI 应用中实时性和结构化输出的难题。希望这次分享能为有类似需求的开发者提供参考。

2840 次点击
所在节点    程序员
12 条回复
freeman12
2025 年 12 月 16 日
试过在前端使用 untruncate-json 流式补全 json
lrwlf
2025 年 12 月 16 日
有个也能实现流式 json 的项目: https://github.com/josdejong/jsonrepair
aloxaf
2025 年 12 月 17 日
不错不错,有的项目在这种场景只是简单地循环调用支持 partial parse 的 json 库

数据量小的时候还好,数据量一高就能明显感到卡顿
tairan2006
2025 年 12 月 17 日
不如发明一种更好的数据格式
wantDoraemon
2025 年 12 月 17 日
@lrwlf 这个看起来是修复 json 。我的那个场景就是 LLM 在输出 JSON 的时候就开始解析,即使 value 还不完整,也需要把内容推给前端
wantDoraemon
2025 年 12 月 17 日
@tairan2006 还有更好的数据格式吗?大模型支持的强数据结构貌似只有 json😂
siweipancc
2025 年 12 月 17 日
当前未支持 \uXXXX Unicode 转义序列
那寄了,面向老外的产品?
wantDoraemon
2025 年 12 月 17 日
@siweipancc 中文是支持的呢 如果有其他字符不支持的,那就得加强一下了,不过我暂时还没遇到这种情况😂
imqiyue
2025 年 12 月 18 日
Yaml 呢?感觉很简洁,很适合
julyclyde
2025 年 12 月 19 日
json/yaml 这种分层嵌套式的语言,能流吗?
wantDoraemon
2025 年 12 月 19 日
@imqiyue
@julyclyde 理论上都可以。但目前大模型能支持强制结构化应该只有 json ,这个方法的出发点不只是需要流式,更是因为既要结构化又要流式输出
julyclyde
2025 年 12 月 20 日
@wantDoraemon 解析可以是流,但是使用这个 dict 的使用方没办法流吧?等于说实际上还是个堵点

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1179289

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX