golang unmarshal复杂的json [英] golang unmarshal complex json
问题描述
我有以下的JSON blob,我试图将它解码为Go。
[contig, 32,{a:[33,41,35],b:[44,34,42]}]
我相信我必须建模JSON的数据结构。我尝试使用一个名为 Line
的结构:
package main
导入(
encoding / json
fmt
)
类型线结构{
重叠字符串
基本字符串
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:can not unmarshal array into Go value of main.Line
我做错了什么?
您指定的JSON输入是一个不同类型的数组,因此您无法将它解组为结构
,但只能分成不同的类型: [] 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))$ b $ 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
被解组为 []接口类型的值{}
你不能简单地转换为 [] int
或 [] float64
因此 for
循环遍历它们,并在每个元素上使用类型断言。另请注意, json
包会将数字编组为< int
(因为不只是整数可以在JSON文本中,所以使用 float64
,它可以同时容纳这两者)。 还要注意,在上例中没有检查类型断言的成功。如果unmarshaled数组少于3个元素,或者任何类型断言失败,就会发生运行时恐慌。
使用 recover()你可以添加一个调用 recover()的 defer
函数,
来捕捉这种恐慌(在去游乐场上试试): 延迟func(){
如果r:= recover(); r!= nil {
fmt.Println(解组失败)
}
}()
l:=行{PopMap:map [string] [ ] int {}}
// ...以下代码使用类型断言
//并将值存储到...
$ $ p
$ b 带检查的代码
或者您可以为类型断言添加检查。类型断言有一个特殊的形式 v,ok = x。(T)
它在使用时永远不会发生混乱,而是如果类型断言不成立, ok
将会是 false
(并且如果类型断言成立,将会是 true
)。
在去游乐场试试:
if len(arr)< 3 {
return
}
var ok bool
l:= Line {PopMap:map [string] [] int {}}
如果l.Contig,ok = ARR [0](字符串)。 !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。 ([]接口{}); !ok {
return
}
pops:= make([] int,len(nums))$ b $ for i,val:= range nums {
if f ,ok:= val。(float64); !ok {
return
} else {
pops [i] = int(f)
}
}
l.PopMap [k] = pops
$ b $ f mt.Printf(%+ v,l)
I have the following JSON blob, and I'm trying to decode it into Go.
["contig", "32", {"a":[33,41,35], "b":[44,34,42]}]
I believe that I have to model the data structure of the JSON. I tried using a struct called 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)
}
I got the following error:
{ map[]}
json: cannot unmarshal array into Go value of type main.Line
What am I doing wrong?
Link to sandbox for trying out code
解决方案 The JSON input you specified is an array of different types, so as it is, you can't unmarshal it into a struct
, but only into a slice of different types: []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)
Output:
[contig 32 map[a:[33 41 35] b:[44 34 42]]]
Filling the struct
Good, you now have the values, just not in the struct
you want them to be. You can use type assertion to obtain the types you want:
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)
Output (try it on the Go Playground):
{Contig:contig Base:32 PopMap:map[a:[33 41 35] b:[44 34 42]]}
Some notes:
The "internal" arrays of the values of "a"
and "b"
are unmarshaled into values of type []interface{}
which you cannot simply convert to []int
or []float64
hence the for
loops to iterate over them and use type assertion on each of their elements. Also note that the json
package unmarshals the numbers into values of type float64
and not int
(because not just integers can be in the JSON text so float64
is used which can accommodate both).
Also note that the success of type assertions are not checked in the above example. If the unmarshaled array has less than 3 elements, or any of the type assertion fails, a runtime panic occurs.
Using recover()
You can add a defer
function which calls recover()
to catch this panic (try it on the 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...
Code with checks
Or you can add checks for the type assertions. The type assertion has a special form v, ok = x.(T)
which when used never panics, but rather if the type assertion doesn't hold, ok
will be false
(and will be true
if type assertion holds).
Try it on the 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)
这篇关于golang unmarshal复杂的json的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
recover()的 defer
函数,
来捕捉这种恐慌(在去游乐场上试试):延迟func(){
$ $ p
如果r:= recover(); r!= nil {
fmt.Println(解组失败)
}
}()
l:=行{PopMap:map [string] [ ] int {}}
// ...以下代码使用类型断言
//并将值存储到...
$ b带检查的代码
或者您可以为类型断言添加检查。类型断言有一个特殊的形式
v,ok = x。(T)
它在使用时永远不会发生混乱,而是如果类型断言不成立,ok
将会是false
(并且如果类型断言成立,将会是true
)。
在去游乐场试试:
if len(arr)< 3 {
return
}
var ok bool
l:= Line {PopMap:map [string] [] int {}}
如果l.Contig,ok = ARR [0](字符串)。 !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。 ([]接口{}); !ok {
return
}
pops:= make([] int,len(nums))$ b $ for i,val:= range nums {
if f ,ok:= val。(float64); !ok {
return
} else {
pops [i] = int(f)
}
}
l.PopMap [k] = pops
$ b $ f mt.Printf(%+ v,l)
I have the following JSON blob, and I'm trying to decode it into Go.
["contig", "32", {"a":[33,41,35], "b":[44,34,42]}]
I believe that I have to model the data structure of the JSON. I tried using a struct called
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) }
I got the following error:
{ map[]} json: cannot unmarshal array into Go value of type main.Line
What am I doing wrong?
Link to sandbox for trying out code
解决方案The JSON input you specified is an array of different types, so as it is, you can't unmarshal it into a
struct
, but only into a slice of different types:[]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)
Output:
[contig 32 map[a:[33 41 35] b:[44 34 42]]]
Filling the
struct
Good, you now have the values, just not in the
struct
you want them to be. You can use type assertion to obtain the types you want: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)
Output (try it on the Go Playground):
{Contig:contig Base:32 PopMap:map[a:[33 41 35] b:[44 34 42]]}
Some notes:
The "internal" arrays of the values of
"a"
and"b"
are unmarshaled into values of type[]interface{}
which you cannot simply convert to[]int
or[]float64
hence thefor
loops to iterate over them and use type assertion on each of their elements. Also note that thejson
package unmarshals the numbers into values of typefloat64
and notint
(because not just integers can be in the JSON text sofloat64
is used which can accommodate both).Also note that the success of type assertions are not checked in the above example. If the unmarshaled array has less than 3 elements, or any of the type assertion fails, a runtime panic occurs.
Using
recover()
You can add a
defer
function which callsrecover()
to catch this panic (try it on the 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...
Code with checks
Or you can add checks for the type assertions. The type assertion has a special form
v, ok = x.(T)
which when used never panics, but rather if the type assertion doesn't hold,ok
will befalse
(and will betrue
if type assertion holds).Try it on the 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)
这篇关于golang unmarshal复杂的json的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!