Reputation: 2728
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?
Upvotes: 9
Views: 11938
Reputation: 1
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
Upvotes: 4
Reputation: 12928
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)
}
Upvotes: 3
Reputation: 12928
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
Upvotes: 1
Reputation: 2294
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.
Upvotes: 0