Golang中动态JSON处理的求助指南
Golang中动态JSON处理的求助指南 我正在尝试解析来自另一个应用程序的一些JSON数据,但遇到了困难。由于传入的JSON格式未知,我无法使用静态结构体。我在网上找到了一些部分有效的代码,但它似乎无法处理最高层级是数组的情况。我不确定如何修复它。以下是代码:
package main
import (
"fmt"
"encoding/json"
)
func get(m interface{}, path ...interface{}) interface{} {
for _, p := range path {
switch idx := p.(type) {
case string:
m = m.(map[string]interface{})[idx]
case int:
m = m.([]interface{})[idx]
}
}
return m
}
func main() {
input := `
[{
"_id": "ansible_facts_localhost",
"data": {
"ansible_iscsi_iqn": "",
"vcenter_centos": {
"msg": "All items completed",
"changed": false,
"results": [
{
"ansible_loop_var": "item",
"instance": {
"hw_name": "mytst01",
"hw_power_status": "poweredOn",
"snapshots": [],
"guest_question": null,
"hw_interfaces": [
"eth0",
"eth1"
],
"hw_guest_id": null,
"current_snapshot": null
}
}
]
}
}
},{
"_id": "ansible_facts_test02",
"data": {
"ansible_processor_count": 2,
"gather_subset": [
"all"
],
"ansible_product_version": "None",
"ansible_service_mgr": "systemd",
"ansible_fibre_channel_wwn": [],
"module_setup": true,
"ansible_memtotal_mb": 7821,
"ansible_hostnqn": "",
"ansible_distribution_version": "7.7",
"ansible_real_user_id": 2750,
"ansible_virtualization_role": "guest",
"ansible_distribution_file_variety": "RedHat",
"ansible_env": {
"_": "/usr/bin/python",
"LESSOPEN": "||/usr/bin/lesspipe.sh %s",
"SSH_CLIENT": "10.0.0.2 36518 22",
"LOGNAME": "ansible",
"USER": "ansible",
"PATH": "/usr/lib64/qt-3.3/bin:/usr/dialogic/bin:/usr/local/bin:/usr/bin:/bin:/opt/wf/bin",
"LANG": "en_US.UTF-8",
"SHELL": "/bin/bash",
"XDG_SESSION_ID": "197",
"HOME": "/home/ansible",
"XDG_RUNTIME_DIR": "/run/user/2750",
"SSH_CONNECTION": "10.0.0.2 36518 10.1.0.2 22",
"SHLVL": "2",
"PWD": "/home/ansible",
"MAIL": "/var/mail/ansible"
}
}
}
]`
JSON := []byte(input)
fmt.Printf("%s\n", JSON)
var d map[string]interface{}
json.Unmarshal(JSON, &d)
fmt.Printf("\n\n get result: \n\n")
//test_path_key := "vcenter_centos"
//fmt.Println(get(d, "data",test_path_key,"results",0,"instance"))
fmt.Println(get(d, 1,"mydata"))
}
目前,当我运行它时,在第14行(位于get函数内)发生恐慌:
get result:
panic: interface conversion: interface {} is map[string]interface {}, not []interface {}
goroutine 1 [running]: main.get(0x4d2f00, 0xc000056360, 0xc000075f68, 0x2, 0x2, 0x0, 0x0) C:/Users/eloy04580.TELETECH/go/src/process_json/process_json.go:14 +0x1e1 main.main() C:/Users/eloy04580.TELETECH/go/src/process_json/process_json.go:99 +0x21c exit status 2
我已经尝试了几种方法来处理这个问题,但似乎没有任何进展。任何帮助都将不胜感激。
更多关于Golang中动态JSON处理的求助指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好,康纳,
感谢你的帮助!啊,我明白了。我在那上面花了太多时间。现在看起来工作正常了。
凯文
更多关于Golang中动态JSON处理的求助指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
有一些第三方包可以用来扁平化 JSON 结构。或者,在处理嵌套和动态的 JSON 结构时,匿名结构体可能会是你的好帮手。请参阅此示例。
我还有一个问题。我需要将这个结构体扁平化,以便最终能将其添加到数据库中。我想做的是遍历它,并构建一个不嵌套当前键的新结构,这样我就可以直接决定要将哪些值添加到数据库中,而无需在多层级结构中迂回操作。有什么想法吗?
谢谢。
你好,
我无法为我的数据定义一个静态结构体。我不知道它会包含什么内容。我知道它是嵌套的,但不知道深度如何,也不知道键是什么。我有一个动态的接口设置。相关的数据在本帖开头。但我需要能够独立引用结构中的每个元素以添加到数据库中,所以仅仅转储整个结构是没有帮助的。
谢谢,
Kevin
嗨 Kevin!
这是一个 Go Playground 链接,里面包含了可以正常运行的你的代码:https://play.golang.org/p/NjHhT-WqOJm
我只是将你的 var d map[string]interface{} 改成了 var d []map[string]interface{},因为你使用的 JSON 是一个 JSON 数组。
然后,我将 switch 语句中 get 方法的第二个 case 改为了: m.([]map[string]interface{})[idx]
我相信现在它应该可以正常工作了。如果你需要我进一步解释我所做的修改,请告诉我。
你的代码在处理动态JSON时遇到了类型断言失败的问题。问题在于你的get函数假设传入的m参数要么是map[string]interface{}要么是[]interface{},但实际上JSON的根节点是一个数组,而你尝试用map[string]interface{}来解析它。
以下是修复后的代码:
package main
import (
"encoding/json"
"fmt"
)
func get(m interface{}, path ...interface{}) (interface{}, error) {
var current interface{} = m
for i, p := range path {
switch idx := p.(type) {
case string:
// 尝试转换为map
if m, ok := current.(map[string]interface{}); ok {
current = m[idx]
} else {
return nil, fmt.Errorf("路径位置 %d: 期望 map[string]interface{}, 得到 %T", i, current)
}
case int:
// 尝试转换为slice
if s, ok := current.([]interface{}); ok {
if idx >= 0 && idx < len(s) {
current = s[idx]
} else {
return nil, fmt.Errorf("路径位置 %d: 索引 %d 超出范围 (长度: %d)", i, idx, len(s))
}
} else {
return nil, fmt.Errorf("路径位置 %d: 期望 []interface{}, 得到 %T", i, current)
}
default:
return nil, fmt.Errorf("路径位置 %d: 不支持的路径类型 %T", i, p)
}
// 检查是否为空
if current == nil {
return nil, fmt.Errorf("路径位置 %d: 路径不存在", i)
}
}
return current, nil
}
func main() {
input := `[{
"_id": "ansible_facts_localhost",
"data": {
"ansible_iscsi_iqn": "",
"vcenter_centos": {
"msg": "All items completed",
"changed": false,
"results": [
{
"ansible_loop_var": "item",
"instance": {
"hw_name": "mytst01",
"hw_power_status": "poweredOn",
"snapshots": [],
"guest_question": null,
"hw_interfaces": [
"eth0",
"eth1"
],
"hw_guest_id": null,
"current_snapshot": null
}
}
]
}
}
},{
"_id": "ansible_facts_test02",
"data": {
"ansible_processor_count": 2,
"gather_subset": [
"all"
],
"ansible_product_version": "None",
"ansible_service_mgr": "systemd",
"ansible_fibre_channel_wwn": [],
"module_setup": true,
"ansible_memtotal_mb": 7821,
"ansible_hostnqn": "",
"ansible_distribution_version": "7.7",
"ansible_real_user_id": 2750,
"ansible_virtualization_role": "guest",
"ansible_distribution_file_variety": "RedHat",
"ansible_env": {
"_": "/usr/bin/python",
"LESSOPEN": "||/usr/bin/lesspipe.sh %s",
"SSH_CLIENT": "10.0.0.2 36518 22",
"LOGNAME": "ansible",
"USER": "ansible",
"PATH": "/usr/lib64/qt-3.3/bin:/usr/dialogic/bin:/usr/local/bin:/usr/bin:/bin:/opt/wf/bin",
"LANG": "en_US.UTF-8",
"SHELL": "/bin/bash",
"XDG_SESSION_ID": "197",
"HOME": "/home/ansible",
"XDG_RUNTIME_DIR": "/run/user/2750",
"SSH_CONNECTION": "10.0.0.2 36518 10.1.0.2 22",
"SHLVL": "2",
"PWD": "/home/ansible",
"MAIL": "/var/mail/ansible"
}
}
}]`
// 使用 interface{} 来解析未知结构的JSON
var data interface{}
if err := json.Unmarshal([]byte(input), &data); err != nil {
fmt.Printf("JSON解析错误: %v\n", err)
return
}
fmt.Println("测试1: 获取第一个元素的_id")
if result, err := get(data, 0, "_id"); err != nil {
fmt.Printf("错误: %v\n", err)
} else {
fmt.Printf("结果: %v\n", result)
}
fmt.Println("\n测试2: 获取第一个元素的data.vcenter_centos.results[0].instance.hw_name")
if result, err := get(data, 0, "data", "vcenter_centos", "results", 0, "instance", "hw_name"); err != nil {
fmt.Printf("错误: %v\n", err)
} else {
fmt.Printf("结果: %v\n", result)
}
fmt.Println("\n测试3: 获取第二个元素的data.ansible_env.PATH")
if result, err := get(data, 1, "data", "ansible_env", "PATH"); err != nil {
fmt.Printf("错误: %v\n", err)
} else {
fmt.Printf("结果: %v\n", result)
}
fmt.Println("\n测试4: 获取第一个元素的data.vcenter_centos.results[0].instance.hw_interfaces[1]")
if result, err := get(data, 0, "data", "vcenter_centos", "results", 0, "instance", "hw_interfaces", 1); err != nil {
fmt.Printf("错误: %v\n", err)
} else {
fmt.Printf("结果: %v\n", result)
}
fmt.Println("\n测试5: 错误路径测试")
if result, err := get(data, 0, "data", "vcenter_centos", "results", 10, "instance"); err != nil {
fmt.Printf("错误: %v\n", err)
} else {
fmt.Printf("结果: %v\n", result)
}
}
主要修改:
-
修复JSON解析:使用
interface{}而不是map[string]interface{}来解析JSON,这样可以处理数组作为根节点的情况。 -
增强
get函数:- 添加了错误处理,返回
(interface{}, error)而不是单一的interface{} - 添加了类型检查,使用类型断言前先检查类型
- 添加了边界检查,防止数组越界
- 添加了路径不存在的检查
- 添加了错误处理,返回
-
改进类型断言:
// 原来的代码(有问题) m = m.(map[string]interface{})[idx] // 修复后的代码 if m, ok := current.(map[string]interface{}); ok { current = m[idx] } -
添加错误信息:为每种可能的错误情况提供清晰的错误信息,便于调试。
这个修复后的代码可以正确处理:
- JSON根节点是数组的情况
- 混合使用字符串键和整数索引的路径
- 路径不存在或类型不匹配的情况
- 数组越界的情况
运行输出示例:
测试1: 获取第一个元素的_id
结果: ansible_facts_localhost
测试2: 获取第一个元素的data.vcenter_centos.results[0].instance.hw_name
结果: mytst01
测试3: 获取第二个元素的data.ansible_env.PATH
结果: /usr/lib64/qt-3.3/bin:/usr/dialogic/bin:/usr/local/bin:/usr/bin:/bin:/opt/wf/bin
测试4: 获取第一个元素的data.vcenter_centos.results[0].instance.hw_interfaces[1]
结果: eth1
测试5: 错误路径测试
错误: 路径位置 4: 索引 10 超出范围 (长度: 1)

