为了账号安全,请及时绑定邮箱和手机立即绑定

golang解组复杂的json

golang解组复杂的json

Go
繁星淼淼 2021-10-18 15:15:38
我有以下 JSON blob,我正在尝试将其解码为 Go。["contig", "32", {"a":[33,41,35], "b":[44,34,42]}]我相信我必须对 JSON 的数据结构进行建模。我尝试使用一个名为的结构Line:package mainimport ("encoding/json""fmt")type Line struct {    Contig string    Base   string    PopMap map[string][]int}func main() {    j := []byte(`["contig", "32", {"a":[33,41,35], "b":[44,34,42]}]`)    var dat Line    err := json.Unmarshal(j, &dat)    fmt.Println(dat)    fmt.Println(err)}我收到以下错误:{  map[]}json: cannot unmarshal array into Go value of type main.Line我究竟做错了什么?
查看完整描述

3 回答

?
DIEA

TA贡献1820条经验 获得超2个赞

您指定的 JSON 输入是不同类型的数组,因此,您无法将其解组为struct,而只能解组为不同类型的切片:[]interface{}。


in := `["contig", "32", {"a":[33,41,35], "b":[44,34,42]}]`


var arr []interface{}

if err := json.Unmarshal([]byte(in), &arr); err != nil {

    panic(err)

}

fmt.Println(arr)

输出:


[contig 32 map[a:[33 41 35] b:[44 34 42]]]

填写 struct

很好,你现在拥有了价值观,只是不在struct你想要的。您可以使用类型断言来获取您想要的类型:


l := Line{PopMap: map[string][]int{}}

l.Contig = arr[0].(string)

l.Base = arr[1].(string)


m := arr[2].(map[string]interface{})

for k, v := range m {

    nums := v.([]interface{})

    pops := make([]int, len(nums))

    for i, val := range nums {

        pops[i] = int(val.(float64))

    }

    l.PopMap[k] = pops

}


fmt.Printf("%+v", l)

输出(在Go Playground上试试):


{Contig:contig Base:32 PopMap:map[a:[33 41 35] b:[44 34 42]]}

一些注意事项:


值的“内部”阵列"a"和"b"被解组到类型的值[]interface{},你不能简单地转换为[]int或[]float64将因此for循环遍历它们和使用类型断言每个自己的元素。另请注意,该json包将数字解组为 typefloat64和 not类型的值int(因为 JSON 文本中不仅可以包含整数,因此float64可以同时使用两者)。


还要注意,在上面的例子中没有检查类型断言是否成功。如果解组的数组少于 3 个元素,或者任何类型断言失败,都会发生运行时恐慌。


使用 recover()

您可以添加一个defer函数来调用recover()以捕获此恐慌(在Go Playground上尝试):


defer func() {

    if r := recover(); r != nil {

        fmt.Println("Failed to unmarshal")

    }

}()


l := Line{PopMap: map[string][]int{}}

// ...and here comes the code that uses type assertions

// and stores values into...

带检查的代码

或者您可以添加对类型断言的检查。类型断言有一种特殊的形式v, ok = x.(T),在使用时不会发生恐慌,而是如果类型断言不成立,ok将会是false(true如果类型断言成立,将会是)。


在Go Playground上试试:


if len(arr) < 3 {

    return

}

var ok bool

l := Line{PopMap: map[string][]int{}}

if l.Contig, ok = arr[0].(string); !ok {

    return

}

if l.Base, ok = arr[1].(string); !ok {

    return

}

if m, ok := arr[2].(map[string]interface{}); !ok {

    return

} else {

    for k, v := range m {

        var nums []interface{}

        if nums, ok = v.([]interface{}); !ok {

            return

        }

        pops := make([]int, len(nums))

        for i, val := range nums {

            if f, ok := val.(float64); !ok {

                return

            } else {

                pops[i] = int(f)

            }

        }

        l.PopMap[k] = pops

    }

}


fmt.Printf("%+v", l)


查看完整回答
反对 回复 2021-10-18
?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

您的 JSON 包含一个数组文字,您正试图将其反序列化为一个结构体。您需要将 JSON 更改为对象文字,其中键是结构的属性名称。


j := []byte(`{

    "Contig": "contig",

    "Base": "32",

    "PopMap": {"a":[33,41,35], "b":[44,34,42]}

}`)

如果 JSON 不是您有能力更改的东西,那么您需要将其反序列化为一个无类型数组,并执行您自己到结构类型的转换。


查看完整回答
反对 回复 2021-10-18
  • 3 回答
  • 0 关注
  • 286 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信