如何在Go中解析不完整/部分JSON?
在Go中解析不完整/部分JSON字符串为map/interface类型
针对OpenAI流式聊天补全返回的分片JSON(比如函数调用的响应被拆分成多段SSE消息),需要实时解析不完整的JSON片段并转换为map[string]any类型,以下是具体实现方案:
核心思路
标准库encoding/json仅支持完整有效的JSON解析,无法处理未闭合的字符串、未完成的键值对等情况。我们需要自定义一个状态机驱动的解析器,逐字符处理输入,维护当前解析状态(如是否处于字符串中、当前正在处理的键/值、已完成的键值对),从而实时输出已解析的有效内容。
实现代码
package main import ( "fmt" "strings" "unicode" ) type PartialJSONParser struct { result map[string]any currentKey string currentValue strings.Builder inString bool escaped bool expectingKey bool expectingVal bool } func NewPartialJSONParser() *PartialJSONParser { return &PartialJSONParser{ result: make(map[string]any), expectingKey: true, } } // Feed 输入一段JSON分片,更新解析状态并返回当前已解析的结果 func (p *PartialJSONParser) Feed(input string) map[string]any { for _, r := range input { switch { case p.inString: if p.escaped { p.currentValue.WriteRune(r) p.escaped = false continue } if r == '\\' { p.escaped = true continue } if r == '"' { p.inString = false if p.expectingKey { p.currentKey = p.currentValue.String() p.currentValue.Reset() p.expectingKey = false p.expectingVal = true } else if p.expectingVal { p.result[p.currentKey] = p.currentValue.String() p.currentValue.Reset() p.expectingVal = false p.expectingKey = true } } else { p.currentValue.WriteRune(r) } case unicode.IsSpace(r): continue case r == '{': // 忽略已处理的开头大括号 case r == '"': p.inString = true case r == ':': // 键已读取完成,准备读取值 p.expectingKey = false p.expectingVal = true case r == ',': // 如果当前正在处理值,说明值是未闭合的字符串,暂存当前内容 if p.expectingVal && p.currentValue.Len() > 0 { p.result[p.currentKey] = p.currentValue.String() p.currentValue.Reset() p.expectingVal = false p.expectingKey = true } case r == '}': // 结束时处理未完成的键值对 if p.expectingVal && p.currentValue.Len() > 0 { p.result[p.currentKey] = p.currentValue.String() } default: // 处理非字符串类型的部分值(如数字、布尔值),这里简化处理为字符串 if p.expectingVal { p.currentValue.WriteRune(r) } } } // 返回当前已解析的结果副本 copyResult := make(map[string]any, len(p.result)) for k, v := range p.result { copyResult[k] = v } // 如果有未完成的键值对,也加入结果 if p.expectingVal && p.currentValue.Len() > 0 { copyResult[p.currentKey] = p.currentValue.String() } return copyResult } func main() { // 模拟OpenAI流式返回的分片JSON chunks := []string{`{"f`, `iel`, `d":`, `"val`, `ue"}`} parser := NewPartialJSONParser() for i, chunk := range chunks { result := parser.Feed(chunk) fmt.Printf("处理第%d段后结果: %v\n", i+1, result) } // 输出: // 处理第1段后结果: map[] // 处理第2段后结果: map[] // 处理第3段后结果: map[] // 处理第4段后结果: map[field:val] // 处理第5段后结果: map[field:value] }
说明
- 状态管理:解析器通过
inString、escaped、expectingKey、expectingVal等状态变量跟踪当前解析位置,处理字符串转义、键值对切换等场景。 - 分片处理:每次调用
Feed方法输入一段JSON分片后,都会返回当前已解析的有效键值对,支持实时更新UI。 - 扩展性:当前实现仅处理字符串类型的值,可根据需求扩展支持数字、布尔值、嵌套对象/数组等类型的部分解析。
内容的提问来源于stack exchange,提问作者Emre Kayan
相关产品推荐
相关产品推荐

