Files
huobao-drama/pkg/utils/json_parser.go
Connor 9600fc542c init
2026-01-12 13:17:11 +08:00

154 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"encoding/json"
"fmt"
"regexp"
"strings"
)
// SafeParseAIJSON 安全地解析AI返回的JSON处理常见的格式问题
// 包括:
// 1. 移除Markdown代码块标记
// 2. 提取JSON对象
// 3. 清理多余的空白和换行
// 4. 尝试修复截断的JSON
// 5. 提供详细的错误信息
func SafeParseAIJSON(aiResponse string, v interface{}) error {
if aiResponse == "" {
return fmt.Errorf("AI返回内容为空")
}
// 1. 移除可能的Markdown代码块标记
cleaned := strings.TrimSpace(aiResponse)
cleaned = regexp.MustCompile("(?m)^```json\\s*").ReplaceAllString(cleaned, "")
cleaned = regexp.MustCompile("(?m)^```\\s*").ReplaceAllString(cleaned, "")
cleaned = strings.TrimSpace(cleaned)
// 2. 提取JSON对象 (查找第一个 { 到最后一个 })
jsonRegex := regexp.MustCompile(`(?s)\{.*\}`)
jsonMatch := jsonRegex.FindString(cleaned)
if jsonMatch == "" {
return fmt.Errorf("响应中未找到有效的JSON对象原始响应: %s", truncateString(aiResponse, 200))
}
// 3. 尝试解析JSON
err := json.Unmarshal([]byte(jsonMatch), v)
if err == nil {
return nil // 解析成功
}
// 4. 如果解析失败尝试修复截断的JSON
fixedJSON := attemptJSONRepair(jsonMatch)
if fixedJSON != jsonMatch {
if err := json.Unmarshal([]byte(fixedJSON), v); err == nil {
return nil // 修复后解析成功
}
}
// 5. 提供详细的错误上下文
if jsonErr, ok := err.(*json.SyntaxError); ok {
errorPos := int(jsonErr.Offset)
start := maxInt(0, errorPos-100)
end := minInt(len(jsonMatch), errorPos+100)
context := jsonMatch[start:end]
marker := strings.Repeat(" ", errorPos-start) + "^"
return fmt.Errorf(
"JSON解析失败: %s\n错误位置附近:\n%s\n%s",
jsonErr.Error(),
context,
marker,
)
}
return fmt.Errorf("JSON解析失败: %w\n原始响应: %s", err, truncateString(jsonMatch, 300))
}
// attemptJSONRepair 尝试修复常见的JSON问题
func attemptJSONRepair(jsonStr string) string {
// 1. 处理未闭合的字符串
// 如果最后一个字符不是 },尝试补全
trimmed := strings.TrimSpace(jsonStr)
// 2. 检查是否有未闭合的引号
if strings.Count(trimmed, `"`)%2 != 0 {
// 有奇数个引号,尝试补全最后一个引号
trimmed += `"`
}
// 3. 统计括号
openBraces := strings.Count(trimmed, "{")
closeBraces := strings.Count(trimmed, "}")
openBrackets := strings.Count(trimmed, "[")
closeBrackets := strings.Count(trimmed, "]")
// 4. 补全未闭合的数组
for i := 0; i < openBrackets-closeBrackets; i++ {
trimmed += "]"
}
// 5. 补全未闭合的对象
for i := 0; i < openBraces-closeBraces; i++ {
trimmed += "}"
}
return trimmed
}
// ExtractJSONFromText 从文本中提取JSON对象或数组
func ExtractJSONFromText(text string) string {
text = strings.TrimSpace(text)
// 移除Markdown代码块
text = regexp.MustCompile("(?m)^```json\\s*").ReplaceAllString(text, "")
text = regexp.MustCompile("(?m)^```\\s*").ReplaceAllString(text, "")
text = strings.TrimSpace(text)
// 查找JSON对象
if idx := strings.Index(text, "{"); idx != -1 {
if lastIdx := strings.LastIndex(text, "}"); lastIdx != -1 && lastIdx > idx {
return text[idx : lastIdx+1]
}
}
// 查找JSON数组
if idx := strings.Index(text, "["); idx != -1 {
if lastIdx := strings.LastIndex(text, "]"); lastIdx != -1 && lastIdx > idx {
return text[idx : lastIdx+1]
}
}
return text
}
// ValidateJSON 验证JSON字符串是否有效
func ValidateJSON(jsonStr string) error {
var js json.RawMessage
return json.Unmarshal([]byte(jsonStr), &js)
}
// Helper functions
func truncateString(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func minInt(a, b int) int {
if a < b {
return a
}
return b
}