diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..45bcb20 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.goalfyagent.ai/lixuan/playground + +go 1.25.3 + +require github.com/buger/jsonparser v1.1.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..92093d7 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= diff --git a/stream_message/main.go b/stream_message/main.go new file mode 100644 index 0000000..41e8e0b --- /dev/null +++ b/stream_message/main.go @@ -0,0 +1,508 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/buger/jsonparser" +) + +func main() { + fmt.Println("=== 从不完整 JSON 中提取字段值的示例 ===") + fmt.Println() + + // 测试案例 1: 不完整的字符串值 + fmt.Println("1. 提取不完整的字符串值:") + incompleteJSON1 := `{"name": "Alice", "info": "hello world this is incomplete` + if value, found := ExtractIncompleteValue(incompleteJSON1, "info"); found { + fmt.Printf(" 提取到的值: %s\n", value) + } else { + fmt.Println(" 未找到该字段") + } + + // 测试案例 2: 完整的字符串后面还有不完整的字段 + fmt.Println("\n2. 提取完整字段和不完整字段:") + incompleteJSON2 := `{"status": "ok", "data": "complete data", "partial": "incomp` + if value, found := ExtractIncompleteValue(incompleteJSON2, "status"); found { + fmt.Printf(" status: %s (完整)\n", value) + } + if value, found := ExtractIncompleteValue(incompleteJSON2, "data"); found { + fmt.Printf(" data: %s (完整)\n", value) + } + if value, found := ExtractIncompleteValue(incompleteJSON2, "partial"); found { + fmt.Printf(" partial: %s (不完整)\n", value) + } + + // 测试案例 3: 处理转义字符 + fmt.Println("\n3. 处理带转义的不完整字符串:") + incompleteJSON3 := `{"text": "hello \"world\" this is \n incomplete` + if value, found := ExtractIncompleteValue(incompleteJSON3, "text"); found { + fmt.Printf(" 提取到的值: %s\n", value) + } + + // 测试案例 4: 数字和布尔值(不完整) + fmt.Println("\n4. 提取不完整的数字值:") + incompleteJSON4 := `{"count": 12345, "price": 99.` + if value, found := ExtractIncompleteValue(incompleteJSON4, "count"); found { + fmt.Printf(" count: %s (完整)\n", value) + } + if value, found := ExtractIncompleteValue(incompleteJSON4, "price"); found { + fmt.Printf(" price: %s (不完整)\n", value) + } + + // 测试案例 5: 嵌套对象(部分不完整) + fmt.Println("\n5. 提取嵌套字段:") + incompleteJSON5 := `{"user": {"name": "Bob", "email": "bob@exam` + if value, found := ExtractNestedValue(incompleteJSON5, "user", "name"); found { + fmt.Printf(" user.name: %s (完整)\n", value) + } + if value, found := ExtractNestedValue(incompleteJSON5, "user", "email"); found { + fmt.Printf(" user.email: %s (不完整)\n", value) + } + + // 测试案例 6: 实际流式场景模拟 + fmt.Println("\n6. 模拟流式接收数据:") + streamChunks := []string{ + `{"id": 1, "message": "`, + `这是一段很长的消息内容`, + `,正在逐步接收中...`, + `","timestamp": 16`, + } + + accumulated := "" + for i, chunk := range streamChunks { + accumulated += chunk + fmt.Printf(" 收到第 %d 块数据: %s\n", i+1, chunk) + + if value, found := ExtractIncompleteValue(accumulated, "message"); found { + fmt.Printf(" -> 当前 message 值: %s\n", value) + } + } + + fmt.Println("\n=== 使用方法总结 ===") + fmt.Println("jsonparser 无法处理不完整的 JSON,") + fmt.Println("所以我们需要手动实现字符串解析来提取不完整字段。") + fmt.Println("上面的 ExtractIncompleteValue 函数可以:") + fmt.Println(" - 从不完整的 JSON 中提取指定 key 的值") + fmt.Println(" - 处理字符串、数字、布尔值等类型") + fmt.Println(" - 处理转义字符") + fmt.Println(" - 即使值不完整也能返回已接收到的部分") +} + +// ExtractIncompleteValue 从不完整的 JSON 中提取指定 key 的值(即使值不完整) +func ExtractIncompleteValue(jsonData, targetKey string) (string, bool) { + // 查找目标 key(带引号) + keyPattern := fmt.Sprintf(`"%s"`, targetKey) + keyIndex := strings.Index(jsonData, keyPattern) + if keyIndex == -1 { + return "", false + } + + // 找到 key 后面的冒号 + colonIndex := strings.Index(jsonData[keyIndex:], ":") + if colonIndex == -1 { + return "", false + } + + // 跳过冒号和空白字符 + startIndex := keyIndex + colonIndex + 1 + for startIndex < len(jsonData) && isWhitespace(jsonData[startIndex]) { + startIndex++ + } + + if startIndex >= len(jsonData) { + return "", false + } + + // 判断值的类型并提取 + switch jsonData[startIndex] { + case '"': + // 字符串类型 + return extractStringValue(jsonData[startIndex+1:]), true + case '{': + // 对象类型 + return extractUntilMatchingBrace(jsonData[startIndex:]), true + case '[': + // 数组类型 + return extractUntilMatchingBracket(jsonData[startIndex:]), true + default: + // 数字、布尔值、null + return extractPrimitiveValue(jsonData[startIndex:]), true + } +} + +// ExtractNestedValue 提取嵌套字段(支持一层嵌套) +func ExtractNestedValue(jsonData string, parentKey, childKey string) (string, bool) { + // 先找到父对象 + parentPattern := fmt.Sprintf(`"%s"`, parentKey) + parentIndex := strings.Index(jsonData, parentPattern) + if parentIndex == -1 { + return "", false + } + + // 找到父对象的开始位置 + colonIndex := strings.Index(jsonData[parentIndex:], ":") + if colonIndex == -1 { + return "", false + } + + startIndex := parentIndex + colonIndex + 1 + for startIndex < len(jsonData) && isWhitespace(jsonData[startIndex]) { + startIndex++ + } + + if startIndex >= len(jsonData) || jsonData[startIndex] != '{' { + return "", false + } + + // 在父对象内查找子字段 + return ExtractIncompleteValue(jsonData[startIndex:], childKey) +} + +// extractStringValue 提取字符串值(处理转义,支持不完整) +func extractStringValue(data string) string { + var result strings.Builder + escaped := false + + for i := 0; i < len(data); i++ { + ch := data[i] + + if escaped { + result.WriteByte(ch) + escaped = false + continue + } + + if ch == '\\' { + escaped = true + result.WriteByte(ch) + continue + } + + if ch == '"' { + // 遇到未转义的引号,字符串结束 + break + } + + result.WriteByte(ch) + } + + return result.String() +} + +// extractPrimitiveValue 提取原始值(数字、布尔、null) +func extractPrimitiveValue(data string) string { + var result strings.Builder + + for i := 0; i < len(data); i++ { + ch := data[i] + + // 遇到这些字符表示值结束 + if ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' { + break + } + + if !isWhitespace(ch) { + result.WriteByte(ch) + } else if result.Len() > 0 { + // 如果已经开始收集内容,遇到空白字符就停止 + break + } + } + + return result.String() +} + +// extractUntilMatchingBrace 提取到匹配的 } 为止 +func extractUntilMatchingBrace(data string) string { + level := 0 + for i := 0; i < len(data); i++ { + switch data[i] { + case '{': + level++ + case '}': + level-- + if level == 0 { + return data[:i+1] + } + } + } + return data // 返回全部(不完整) +} + +// extractUntilMatchingBracket 提取到匹配的 ] 为止 +func extractUntilMatchingBracket(data string) string { + level := 0 + for i := 0; i < len(data); i++ { + switch data[i] { + case '[': + level++ + case ']': + level-- + if level == 0 { + return data[:i+1] + } + } + } + return data // 返回全部(不完整) +} + +// isWhitespace 判断是否为空白字符 +func isWhitespace(ch byte) bool { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' +} + +func sample() { + // 示例 JSON 数据 + data := []byte(`{ + "person": { + "name": { + "first": "张", + "last": "三" + }, + "age": 30, + "github": "https://github.com/zhangsan", + "email": "zhangsan@example.com" + }, + "company": "Example Corp", + "skills": ["Go", "Python", "JavaScript"], + "scores": [85, 92, 78, 95], + "active": true, + "balance": 1234.56, + "metadata": null, + "tags": { + "level": "senior", + "department": "engineering" + } + }`) + + fmt.Println("=== buger/jsonparser API 示例 ===") + fmt.Println() + + // 1. Get() - 获取字符串值(返回 value, dataType, offset, err) + fmt.Println("1. Get() - 获取简单值") + if value, dataType, _, err := jsonparser.Get(data, "company"); err == nil { + fmt.Printf(" company: %s (类型: %s)\n", string(value), dataType) + } + + // 2. 获取嵌套对象的值 + fmt.Println("\n2. Get() - 获取嵌套对象的值") + if firstName, _, _, err := jsonparser.Get(data, "person", "name", "first"); err == nil { + fmt.Printf(" first name: %s\n", string(firstName)) + } + if lastName, _, _, err := jsonparser.Get(data, "person", "name", "last"); err == nil { + fmt.Printf(" last name: %s\n", string(lastName)) + } + + // 3. GetInt() - 获取整数值 + fmt.Println("\n3. GetInt() - 获取整数值") + if age, err := jsonparser.GetInt(data, "person", "age"); err == nil { + fmt.Printf(" age: %d 岁\n", age) + } + + // 4. GetFloat() - 获取浮点数值 + fmt.Println("\n4. GetFloat() - 获取浮点数值") + if balance, err := jsonparser.GetFloat(data, "balance"); err == nil { + fmt.Printf(" balance: ¥%.2f\n", balance) + } + + // 5. GetBoolean() - 获取布尔值 + fmt.Println("\n5. GetBoolean() - 获取布尔值") + if active, err := jsonparser.GetBoolean(data, "active"); err == nil { + fmt.Printf(" active: %t\n", active) + } + + // 6. ArrayEach() - 遍历字符串数组 + fmt.Println("\n6. ArrayEach() - 遍历数组") + fmt.Print(" skills: ") + jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + fmt.Printf("%s ", string(value)) + }, "skills") + fmt.Println() + + // 7. 遍历数字数组并计算总和 + fmt.Println("\n7. ArrayEach() - 处理数字数组") + var total int64 + var count int + jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + if num, err := jsonparser.ParseInt(value); err == nil { + total += num + count++ + fmt.Printf(" [%d] = %d\n", count-1, num) + } + }, "scores") + if count > 0 { + fmt.Printf(" 统计: 总和=%d, 平均分=%.2f\n", total, float64(total)/float64(count)) + } + + // 8. GetUnsafeString() - 获取字符串(零拷贝,性能最好) + fmt.Println("\n8. GetUnsafeString() - 高性能字符串获取(零拷贝)") + if github, err := jsonparser.GetUnsafeString(data, "person", "github"); err == nil { + fmt.Printf(" github: %s\n", github) + } + fmt.Println(" 注意: GetUnsafeString 返回的字符串指向原始 JSON 数据,不会分配新内存") + + // 9. ObjectEach() - 遍历对象的所有键值对 + fmt.Println("\n9. ObjectEach() - 遍历对象") + fmt.Println(" person.name 的所有字段:") + jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { + fmt.Printf(" %s: %s (类型: %s)\n", string(key), string(value), dataType) + return nil + }, "person", "name") + + // 10. Set() - 设置/修改值 + fmt.Println("\n10. Set() - 修改 JSON 值") + newData, err := jsonparser.Set(data, []byte(`"Example Tech Inc."`), "company") + if err == nil { + if company, _, _, err2 := jsonparser.Get(newData, "company"); err2 == nil { + fmt.Printf(" 修改后的 company: %s\n", string(company)) + } + } + + // 11. Delete() - 删除键 + fmt.Println("\n11. Delete() - 删除键") + newData = jsonparser.Delete(data, "metadata") + fmt.Println(" 已删除 metadata 字段") + // 验证删除 + if _, _, _, err := jsonparser.Get(newData, "metadata"); err != nil { + fmt.Printf(" 验证: metadata 已不存在 (%v)\n", err) + } + + // 12. GetString() - 直接获取字符串(会分配新内存) + fmt.Println("\n12. GetString() - 获取字符串值") + if company, err := jsonparser.GetString(data, "company"); err == nil { + fmt.Printf(" company: %s\n", company) + } + if email, err := jsonparser.GetString(data, "person", "email"); err == nil { + fmt.Printf(" email: %s\n", email) + } + + // 13. EachKey() - 批量获取多个键(高性能,只遍历一次 JSON) + fmt.Println("\n13. EachKey() - 批量获取多个键(一次遍历)") + paths := [][]string{ + {"company"}, + {"person", "age"}, + {"active"}, + {"balance"}, + } + jsonparser.EachKey(data, func(idx int, value []byte, vt jsonparser.ValueType, err error) { + switch idx { + case 0: + fmt.Printf(" company: %s\n", string(value)) + case 1: + fmt.Printf(" age: %s\n", string(value)) + case 2: + fmt.Printf(" active: %s\n", string(value)) + case 3: + fmt.Printf(" balance: %s\n", string(value)) + } + }, paths...) + fmt.Println(" 注意: EachKey 比多次调用 Get 更高效") + + // 14. 数据类型检查 - Get 返回 4 个值 + fmt.Println("\n14. Get() 返回值详解") + value, dataType, offset, err := jsonparser.Get(data, "person", "age") + if err == nil { + fmt.Printf(" 值: %s\n", string(value)) + fmt.Printf(" 类型: %s\n", dataType) + fmt.Printf(" 偏移量: %d\n", offset) + fmt.Println(" 注意: Get 返回 (value []byte, dataType ValueType, offset int, err error)") + } + + // 15. 处理数组中的对象 + fmt.Println("\n15. 处理复杂的嵌套结构") + complexData := []byte(`{ + "users": [ + {"id": 1, "name": "Alice", "role": "admin"}, + {"id": 2, "name": "Bob", "role": "user"}, + {"id": 3, "name": "Charlie", "role": "user"} + ] + }`) + fmt.Println(" users:") + jsonparser.ArrayEach(complexData, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { + id, _ := jsonparser.GetInt(value, "id") + name, _ := jsonparser.GetString(value, "name") + role, _ := jsonparser.GetString(value, "role") + fmt.Printf(" ID: %d, Name: %-8s, Role: %s\n", id, name, role) + }, "users") + + // 16. 错误处理示例 + fmt.Println("\n16. 错误处理") + if _, _, _, err := jsonparser.Get(data, "nonexistent"); err != nil { + fmt.Printf(" 尝试获取不存在的键: %v\n", err) + } + + // 17. 处理 null 值 + fmt.Println("\n17. 处理 null 值") + if _, dataType, _, err := jsonparser.Get(data, "metadata"); err == nil { + if dataType == jsonparser.Null { + fmt.Printf(" metadata 是 null 值 (类型: %s)\n", dataType) + } + } + + // 18. ObjectEach 遍历整个对象 + fmt.Println("\n18. ObjectEach() - 遍历 tags 对象的所有字段") + jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { + fmt.Printf(" %s: %s\n", string(key), string(value)) + return nil + }, "tags") + + // 19. 使用 ParseInt 和 ParseFloat + fmt.Println("\n19. ParseInt/ParseFloat - 解析字节数组") + ageBytes, _, _, _ := jsonparser.Get(data, "person", "age") + if ageInt, err := jsonparser.ParseInt(ageBytes); err == nil { + fmt.Printf(" ParseInt: age = %d\n", ageInt) + } + balanceBytes, _, _, _ := jsonparser.Get(data, "balance") + if balanceFloat, err := jsonparser.ParseFloat(balanceBytes); err == nil { + fmt.Printf(" ParseFloat: balance = %.2f\n", balanceFloat) + } + + // 20. 判断数据类型 + fmt.Println("\n20. ValueType - 所有数据类型") + checkType(data, "person", "name", "first") // String + checkType(data, "person", "age") // Number + checkType(data, "active") // Boolean + checkType(data, "metadata") // Null + checkType(data, "skills") // Array + checkType(data, "person") // Object + + fmt.Println("\n=== 示例完成 ===") + fmt.Println("\n性能提示:") + fmt.Println(" - GetUnsafeString: 零拷贝,最快,但返回的字符串生命周期与原始数据相同") + fmt.Println(" - GetString: 分配新内存,安全,但稍慢") + fmt.Println(" - EachKey: 批量获取多个键时,只遍历一次 JSON,比多次 Get 快") + fmt.Println(" - ArrayEach/ObjectEach: 流式处理,内存占用小") +} + +// checkType 辅助函数:检查并打印数据类型 +func checkType(data []byte, keys ...string) { + if _, dataType, _, err := jsonparser.Get(data, keys...); err == nil { + path := keys[0] + for i := 1; i < len(keys); i++ { + path += "." + keys[i] + } + + var typeName string + switch dataType { + case jsonparser.String: + typeName = "String (字符串)" + case jsonparser.Number: + typeName = "Number (数字)" + case jsonparser.Object: + typeName = "Object (对象)" + case jsonparser.Array: + typeName = "Array (数组)" + case jsonparser.Boolean: + typeName = "Boolean (布尔)" + case jsonparser.Null: + typeName = "Null (空值)" + case jsonparser.Unknown: + typeName = "Unknown (未知)" + default: + typeName = "NotExist (不存在)" + } + + fmt.Printf(" %s -> %s\n", path, typeName) + } +}