将JSON数组反序列化为结构体。

9

我有一个自定义值的数组

[
    1,
    "test",
    { "a" : "b" }
]

我可以将其反序列化为 []interface{},但这不是我想要的。

我希望将此数组反序列化为结构体。

type MyType struct {
    Count int
    Name string
    Relation map[string]string
}

在Go语言中,使用标准库或者其他库是否可以实现这个功能?

http://golang.org/pkg/encoding/json/ 解释了细节并包含示例。 - Volker
1
@Volker,我之前看过,但没有找到解决我的问题的方法。您能指出具体在哪里解释如何解决这样的问题吗?是哪个例子? - Alexander Ponomarev
4个回答

4
其他答案看起来太复杂了,这里提供另一种方法:
package main

import (
   "encoding/json"
   "fmt"
)

type myType struct {
   count int
   name string
   relation map[string]string
}

func (t *myType) UnmarshalJSON(b []byte) error {
   a := []interface{}{&t.count, &t.name, &t.relation}
   return json.Unmarshal(b, &a)
}

func main() {
   var t myType
   json.Unmarshal([]byte(`[1, "test", {"a": "b"}]`), &t)
   fmt.Printf("%+v\n", t)
}

https://eagain.net/articles/go-json-array-to-struct


3
您可以使用github.com/ugorji/go/codec,它可以将数组解组为结构体:

将结构体编码为数组,并从数据流中解码结构体

尽管该库宣传是“encoding/json的即插即用替代品”,但这仅适用于json:标记。因此,您需要使用codec.Decoder而不是json.Unmarshal

package main

import "fmt"
import "github.com/ugorji/go/codec"

type MyType struct {
    Count    int
    Name     string
    Relation map[string]string
}

func main() {
    x := &MyType{}
    data := []byte(`[1,"test",{"a":"b"}]`)
    codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(x)
    fmt.Println(x)
}

你能在你的回答中提供一个例子吗? - Alexander Ponomarev

1
那是一个元组,将元组解包到结构体中是完全可以的,但是 encoding/json 不支持这样做。
然而,我们可以使用以下辅助函数,它遍历结构体的字段并对其进行解析:
// UnmarshalJSONTuple unmarshals JSON list (tuple) into a struct.
func UnmarshalJSONTuple(text []byte, obj interface{}) (err error) {
    var list []json.RawMessage
    err = json.Unmarshal(text, &list)
    if err != nil {
        return
    }

    objValue := reflect.ValueOf(obj).Elem()
    if len(list) > objValue.Type().NumField() {
        return fmt.Errorf("tuple has too many fields (%v) for %v",
            len(list), objValue.Type().Name())
    }

    for i, elemText := range list {
        err = json.Unmarshal(elemText, objValue.Field(i).Addr().Interface())
        if err != nil {
            return
        }
    }
    return
}

所以你只需要提供UnmarshalJSON方法:

func (this *MyType) UnmarshalJSON(text []byte) (err error) {
    return UnmarshalJSONTuple(text, this)
}

这是完整的示例:http://play.golang.org/p/QVA-1ynn15

0

由于您的 JSON 数组中包含不同类型的值,因此无法简单地使用 Go 解析它。如果您可以控制 JSON 输入的格式,请将三个值包装在 {} 中以形成对象,例如:

[
    {
        "Count": 1,
        "Name": "test",
        "Relation": { "a" : "b" }
     }
]

然后解析到你的结构体应该可以工作。

如果您无法控制json输入。将其解析为[]interface{},然后手动将值分配给您的结构体。尽管这可能会变得棘手,具体取决于您想支持的可能响应的复杂性。

请注意,此问题指向golang json解析方法的核心限制,因此 - 就我所知 - 它也不能通过库来解决。


据我所见,它不起作用,您提供的JSON无效 :( 手动分配值很复杂,我正在寻找一个可以简化此过程的库。 - Alexander Ponomarev
你说得对,我在json对象中漏掉了键,对此感到抱歉。通常情况下,如果你遇到这样的问题,我建议使用目标值的虚拟实例并对其进行编码,以查看go生成的输出类型(因此期望作为“解码”的输入)。 - tike
请看 https://github.com/likexian/simplejson,但我认为它也无法解决您的问题。 - tike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接