简体   繁体   English

golang unmarshal complex json

[英]golang unmarshal complex json

I have the following JSON blob, and I'm trying to decode it into Go. 我有以下JSON blob,我正在尝试将其解码为Go。

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

I believe that I have to model the data structure of the JSON. 我相信我必须建模JSON的数据结构。 I tried using a struct called Line : 我尝试使用一个名为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{} . 您指定的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)

Output: 输出:

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

Filling the struct 填充struct

Good, you now have the values, just not in the struct you want them to be. 好的,你现在拥有了这些值,而不是你想要它们的struct 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 ): 输出(在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. "a""b"值的“内部”数组被解组为[]interface{}类型的值,你不能简单地转换为[]int[]float64因此for循环迭代它们并使用在每个元素上键入断言。 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). 另请注意, json包将数字解组为float64类型的值而不是int (因为不仅整数可以在JSON文本中,因此使用float64可以容纳两者)。

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. 如果unmarshaled数组少于3个元素,或者任何类型断言失败,则会发生运行时混乱。

Using recover() 使用recover()

You can add a defer function which calls recover() to catch this panic (try it on the Go Playground ): 你可以添加一个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...

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). 类型断言有一个特殊的形式v, ok = x.(T) ,当使用时从不发生恐慌,而是如果类型断言不成立, ok将为false (如果类型断言成立,则为true )。

Try it on the Go Playground : 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)

Your JSON contains an array literal and you're trying to deserialize it as a struct. 您的JSON包含一个数组文字,并且您尝试将其反序列化为结构。 You need to change the JSON to an object literal where the keys are the property names of your struct. 您需要将JSON更改为对象文字,其中键是结构的属性名称。

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

If the JSON isn't something you have the ability to change, then you will need to deserialize it into an untyped array and perform your own transformation into the struct type. 如果JSON不是您能够更改的东西,那么您需要将其反序列化为无类型数组并执行您自己的转换为结构类型。

Because you have an array literal rather than an object, the best way to parse is by unmarshaling first to a slice of json.RawMessages , and then go through the fields in the resulting slice: 因为你有一个数组文字而不是一个对象,解析的最好方法是首先解组到一片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)
}

This gives the correct result: 这给出了正确的结果:

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

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

If you have control over the source of the JSON though, changing it to an object literal would be the most straightforward way to go about it. 如果您可以控制JSON的源代码,那么将其更改为对象文字将是最简单的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM