
当 API 返回的 JSON 中某个字段本应是数组却被序列化为字符串(如 "content":"[{\"id\":\"123\"}]"),需先将其类型断言为 string,再用 json.Unmarshal 二次解析,才能正确转换为 []map[string]interface{} 或 []map[string]string。
当 api 返回的 json 中某个字段本应是数组却被序列化为字符串(如 `"content":"[{\"id\":\"123\"}]"`),需先将其类型断言为 string,再用 `json.unmarshal` 二次解析,才能正确转换为 `[]map[string]interface{}` 或 `[]map[string]string`。
Go 的 encoding/json 包默认按 JSON Schema 严格解析:若字段值被双引号包裹(如 "[{\"id\":\"567670128\"}]"),它会被识别为原始字符串而非数组。直接类型断言为 []interface{} 必然失败,并触发 panic —— 正如示例中 interface conversion: interface is string, not []interface {} 所提示。
解决方法是两步解析:
- 先将顶层 JSON 解析为 map[string]interface{},获取 "content" 字段并断言其为 string;
- 再将该字符串作为独立 JSON 输入,调用 json.Unmarshal 解析为目标结构。
以下是推荐的完整实现:
package main
import (
"fmt"
"encoding/json"
)
func check(e error) {
if e != nil {
panic(e)
}
}
var incomingJson = `{
"status":"success",
"content":"[{\"id\":\"567670128\"},{\"id\":\"567670129\"}]"
}`
func main() {
// 第一步:解析外层 JSON
var decoded map[string]interface{}
err := json.Unmarshal([]byte(incomingJson), &decoded)
check(err)
// 第二步:提取 content 字段并断言为 string
contentStr, ok := decoded["content"].(string)
if !ok {
panic("content field is not a string")
}
// 第三步:将 content 字符串再次解析为 []map[string]interface{}
var contentObjects []map[string]interface{}
err = json.Unmarshal([]byte(contentStr), &contentObjects)
check(err)
// 安全访问第一个对象
if len(contentObjects) > 0 {
fmt.Printf("First object: %+v\n", contentObjects[0])
// 输出:First object: map[id:567670128]
}
}
✅ 最佳实践建议:
- 始终校验类型断言结果(如 contentStr, ok := ...),避免运行时 panic;
- 若确定 content 中所有值均为字符串(如 id、name 等),可直接使用 []map[string]string 提升类型安全性;
- 对于高频调用场景,建议封装为可复用函数,例如:
func parseNestedJSON[T any](data interface{}, key string) ([]T, error) {
str, ok := data.(map[string]interface{})[key].(string)
if !ok {
return nil, fmt.Errorf("field %q is not a valid string", key)
}
var result []T
return result, json.Unmarshal([]byte(str), &result)
}
⚠️ 注意事项:
- 此类“字符串内嵌 JSON”属于非标准设计,理想方案应推动 API 提供符合 JSON Schema 的原生数组字段;
- 若 content 字符串含非法 JSON(如转义错误、编码不一致),Unmarshal 将返回具体错误,务必捕获处理;
- 避免多次 []byte() 转换造成内存开销,生产环境可考虑复用 []byte 缓冲区或使用 json.RawMessage 优化性能。
通过两次解码,你就能安全、清晰地将“伪数组字符串”转化为真正的 Go 结构体,实现健壮的数据消费逻辑。











