将 JSON 数组解组为结构体。

huangapple go评论89阅读模式
英文:

Unmarshal json array to struct

问题

我有一个自定义值的数组:

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

我可以将其解组为 []interface{},但这不是我想要的。

我想将这个数组解组为结构体:

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

在Go语言中,是否可以使用标准库或第三方库实现这个功能?

英文:

I have an array of custom values

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

I can unmarshal in to []interface{}, but it's not what I want.

I would like to unmarshal this array to struct

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

Is it possible in Go with standard or side libraries?

答案1

得分: 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

英文:

The other answers seem too complicated, here is another approach:

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

答案2

得分: 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)
}
英文:

You can use github.com/ugorji/go/codec, it can unmarshal array to a struct:

> Encode a struct as an array, and decode struct from an array in the data stream

Although the library advertises "drop-in replacement for encoding/json" - it's only about the json: tag. So you have to use codec.Decoder instead of 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)
}

答案3

得分: 1

这是一个元组,将元组解组为结构体是完全可以的,只是encoding/json不支持这样做。

不过我们可以使用以下辅助函数,它会遍历结构体的字段并将其解组:

// UnmarshalJSONTuple 将 JSON 列表(元组)解组为结构体。
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("元组的字段数过多(%v),超过了 %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

英文:

That is a tuple, and it's perfectly fine to unmarshal tuple into a structure, except that encoding/json doesn't support that.

However we can use the following helper function, which iterates over the fields of the structure and unmarshals them:

// 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
}

So you only need to provide the UnmarshalJSON method:

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

Here is the complete example: http://play.golang.org/p/QVA-1ynn15

答案4

得分: 0

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

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

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

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

请注意,这个问题指向了 Golang JSON 解析方法的一个核心限制,因此据我所知,它也无法通过库来解决。

英文:

since your json holds values of different types in an array it is not possible to parse this with go simply. If you have controll over how the json input is formatted, wrap the three values in {} to form an object, like so:

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

Then parsing into your struct should work.

If you have no controll over the json input. Parse it as []interface{} and then assign the values to your struct manually. Even though this might get tricky, depending on complexity of possible responses you'd like to support.

Please note, that this issue points to a core limitation of golangs json parsing method and that therefore - as far as I know - it can also not be solved by libraries.

huangapple
  • 本文由 发表于 2014年3月20日 17:39:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/22528820.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定