Golang如何解析含可变类型的JSON为Map?Salt-Stack API解析报错
在Go中解析值类型可变的Salt-Stack API JSON响应
嘿,我完全懂你碰到的这个坑——Salt-Stack API返回的JSON里,同一个Map的value一会儿是布尔值,一会儿又是字符串错误信息,直接用map[string]bool反序列化肯定炸锅。下面给你几个实用的解决方案,按你的需求选就行:
方法1:用map[string]interface{}直接接收
这是最直接的方案,因为interface{}可以容纳任意类型的值,完美适配这种混合类型的场景。示例代码如下:
package main import ( "encoding/json" "fmt" ) type getMinionsStatusResponse struct { Returns []map[string]interface{} `json:"return"` } func main() { jsonData := []byte(`{"return": [{"<UUID1>": true, "<UUID2>": "Minion did not return. [No response]", "<UUID3>": true, "<UUID4>": false}]}`) var resp getMinionsStatusResponse err := json.Unmarshal(jsonData, &resp) if err != nil { panic(err) } // 遍历解析后的结果,判断每个值的类型 for _, minionMap := range resp.Returns { for uuid, value := range minionMap { switch v := value.(type) { case bool: fmt.Printf("Minion %s 状态: %t\n", uuid, v) case string: fmt.Printf("Minion %s 错误信息: %s\n", uuid, v) default: fmt.Printf("Minion %s 出现未知类型的值: %v\n", uuid, v) } } } }
这种方式的优点是灵活,能应对所有类型的value;缺点是需要手动做类型断言,每次使用值都要判断类型。
方法2:自定义UnmarshalJSON封装解析逻辑
如果你想让上层代码更简洁,不用每次都写类型判断,可以给结构体实现json.Unmarshaler接口,把解析逻辑封装起来。示例:
package main import ( "encoding/json" "fmt" ) // 定义一个更易用的Minion状态结构体 type MinionStatus struct { UUID string IsOnline bool ErrorMsg string } type getMinionsStatusResponse struct { Minions []MinionStatus `json:"-"` // 让JSON解析忽略这个字段,我们自己处理 } // 实现json.Unmarshaler接口 func (r *getMinionsStatusResponse) UnmarshalJSON(data []byte) error { // 先解析成原始的混合类型结构 var rawResp struct { Returns []map[string]interface{} `json:"return"` } if err := json.Unmarshal(data, &rawResp); err != nil { return err } // 转换为我们定义的MinionStatus切片 r.Minions = make([]MinionStatus, 0) for _, minionMap := range rawResp.Returns { for uuid, val := range minionMap { ms := MinionStatus{UUID: uuid} switch v := val.(type) { case bool: ms.IsOnline = v case string: ms.IsOnline = false ms.ErrorMsg = v } r.Minions = append(r.Minions, ms) } } return nil } func main() { jsonData := []byte(`{"return": [{"<UUID1>": true, "<UUID2>": "Minion did not return. [No response]", "<UUID3>": true, "<UUID4>": false}]}`) var resp getMinionsStatusResponse err := json.Unmarshal(jsonData, &resp) if err != nil { panic(err) } // 现在可以直接使用结构化的Minion状态数据了 for _, ms := range resp.Minions { if ms.IsOnline { fmt.Printf("Minion %s 在线\n", ms.UUID) } else { fmt.Printf("Minion %s 离线,原因: %s\n", ms.UUID, ms.ErrorMsg) } } }
这种方式把解析逻辑封装在结构体内部,上层代码拿到的就是清晰的结构化数据,非常适合在项目中长期使用。
方法3:用json.RawMessage延迟解析
如果你的场景需要更精细的解析控制,或者只有部分值需要处理,可以用json.RawMessage先保留原始的JSON字节,之后再按需解析:
package main import ( "encoding/json" "fmt" ) type getMinionsStatusResponse struct { Returns []map[string]json.RawMessage `json:"return"` } func main() { jsonData := []byte(`{"return": [{"<UUID1>": true, "<UUID2>": "Minion did not return. [No response]", "<UUID3>": true, "<UUID4>": false}]}`) var resp getMinionsStatusResponse err := json.Unmarshal(jsonData, &resp) if err != nil { panic(err) } for _, minionMap := range resp.Returns { for uuid, raw := range minionMap { // 先尝试解析成布尔值 var status bool if err := json.Unmarshal(raw, &status); err == nil { fmt.Printf("Minion %s 状态: %t\n", uuid, status) continue } // 布尔值解析失败,尝试解析成字符串错误信息 var errMsg string if err := json.Unmarshal(raw, &errMsg); err == nil { fmt.Printf("Minion %s 错误: %s\n", uuid, errMsg) continue } fmt.Printf("Minion %s 的值无法解析为布尔或字符串类型\n", uuid) } } }
这种方式适合需要针对不同类型做特殊处理的场景,或者不确定value类型的情况。
内容的提问来源于stack exchange,提问作者user892960
相关产品推荐
相关产品推荐

