golang unmarshal complex json

时间:2015-06-30 04:08:43

标签: json go unmarshalling

我有以下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

我做错了什么?

Link to sandbox for trying out code

3 个答案:

答案 0 :(得分:6)

您指定的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,因此{{1} }循环迭代它们并在每个元素上使用类型断言。另请注意,for包将数字解组为类型json而不是float64的值(因为不只是整数可以在JSON文本中,因此使用int可以使用float64同时兼顾)。

另请注意,在上面的示例中未检查类型断言是否成功。如果unmarshaled数组少于3个元素,或者任何类型断言失败,则会发生运行时混乱。

使用recover()

您可以添加一个调用defer的{​​{1}}函数来捕捉此恐慌(在Go Playground上尝试):

recover()

带支票的代码

或者您可以添加对类型断言的检查。类型断言有一个特殊的形式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(并且将是{{1}如果类型断言成立)。

Go Playground

上试试
false

答案 1 :(得分:1)

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

j := []byte(`{
    "Contig": "contig",
    "Base": "32",
    "PopMap": {"a":[33,41,35], "b":[44,34,42]}
}`)

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

答案 2 :(得分:1)

因为你有一个数组文字而不是一个对象,所以解析的最佳方法是首先解组为json.RawMessages切片,然后遍历生成切片中的字段:

contig
32
{[33 41 35] [44 34 42]}

这给出了正确的结果:

$(document).ready(function(){ $('body').delegate("select[name^='cat']","change",function(){ if($(this).val() == 0){ //first option $('select').prop( "disabled", false); //enable all select box //disable the very next one $(this).next().prop( "disabled", true ); } }); });

游乐场:https://play.golang.org/p/jcYvINkTTn

如果您可以控制JSON的源代码,那么将其更改为对象文字将是最简单的方法。