我有以下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
我做错了什么?
发布于 2015-06-30 06:21:12
您指定的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
中。您可以使用type assertion获取所需的类型:
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
包将数字解组为float64
类型的值,而不是int
类型的值(因为不只是整数可以包含在JSON文本中,所以使用了float64
,它可以同时容纳这两种类型)。
还要注意,在上面的示例中没有检查类型断言的成功。如果未封送的数组的元素少于3个,或者任何类型断言失败,则会发生运行时死机。
使用recover()
您可以添加一个调用recover()
的defer
函数来捕获这种死机(在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)
发布于 2015-06-30 04:39:00
因为您拥有的是数组文字而不是对象,所以解析的最佳方法是首先解组到json.RawMessages的片段,然后遍历结果片段中的字段:
package main
import (
"encoding/json"
"fmt"
)
func main() {
j := []byte(`["contig", "32", {"a":[33,41,35], "b":[44,34,42]}]`)
var entries []json.RawMessage
err := json.Unmarshal(j, &entries)
if err != nil {
fmt.Println(err)
}
var contig string
var num string
var obj struct {
A []int `json:"a"`
B []int `json:"b"`
}
err = json.Unmarshal(entries[0], &contig)
err = json.Unmarshal(entries[1], &num)
err = json.Unmarshal(entries[2], &obj)
fmt.Println(contig)
fmt.Println(num)
fmt.Println(obj)
}
这会给出正确的结果:
contig 32 {[33 41 35] [44 34 42]}
游乐场:https://play.golang.org/p/jcYvINkTTn
但是,如果您可以控制JSON的源代码,那么将其更改为对象文字将是最直接的方法。
发布于 2015-06-30 04:29:29
您的JSON包含一个数组文字,而您正试图将其反序列化为结构。您需要将JSON更改为对象文字,其中键是结构的属性名称。
j := []byte(`{
"Contig": "contig",
"Base": "32",
"PopMap": {"a":[33,41,35], "b":[44,34,42]}
}`)
如果您无法更改JSON,则需要将其反序列化为无类型数组,并将其转换为结构类型。
https://stackoverflow.com/questions/31129379
复制