509 lines
16 KiB
Go
509 lines
16 KiB
Go
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)
|
||
}
|
||
}
|