猿问

golang解组复杂的json

我有以下 JSON blob,我正在尝试将其解码为 Go。


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

我相信我必须对 JSON 的数据结构进行建模。我尝试使用一个名为的结构Line:


package main


import (

"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

我究竟做错了什么?


繁星淼淼
浏览 287回答 3
3回答

DIEA

您指定的 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 {&nbsp; &nbsp; 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 {&nbsp; &nbsp; nums := v.([]interface{})&nbsp; &nbsp; pops := make([]int, len(nums))&nbsp; &nbsp; for i, val := range nums {&nbsp; &nbsp; &nbsp; &nbsp; pops[i] = int(val.(float64))&nbsp; &nbsp; }&nbsp; &nbsp; 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() {&nbsp; &nbsp; if r := recover(); r != nil {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Failed to unmarshal")&nbsp; &nbsp; }}()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 {&nbsp; &nbsp; return}var ok booll := Line{PopMap: map[string][]int{}}if l.Contig, ok = arr[0].(string); !ok {&nbsp; &nbsp; return}if l.Base, ok = arr[1].(string); !ok {&nbsp; &nbsp; return}if m, ok := arr[2].(map[string]interface{}); !ok {&nbsp; &nbsp; return} else {&nbsp; &nbsp; for k, v := range m {&nbsp; &nbsp; &nbsp; &nbsp; var nums []interface{}&nbsp; &nbsp; &nbsp; &nbsp; if nums, ok = v.([]interface{}); !ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; pops := make([]int, len(nums))&nbsp; &nbsp; &nbsp; &nbsp; for i, val := range nums {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if f, ok := val.(float64); !ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pops[i] = int(f)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; l.PopMap[k] = pops&nbsp; &nbsp; }}fmt.Printf("%+v", l)

料青山看我应如是

您的 JSON 包含一个数组文字,您正试图将其反序列化为一个结构体。您需要将 JSON 更改为对象文字,其中键是结构的属性名称。j := []byte(`{&nbsp; &nbsp; "Contig": "contig",&nbsp; &nbsp; "Base": "32",&nbsp; &nbsp; "PopMap": {"a":[33,41,35], "b":[44,34,42]}}`)如果 JSON 不是您有能力更改的东西,那么您需要将其反序列化为一个无类型数组,并执行您自己到结构类型的转换。
随时随地看视频慕课网APP

相关分类

Go
我要回答