I have the following JSON
{"a":1, "b":2, "?":1, "??":1}
I know that it has the "a" and "b" fields, but I don't know the names of other fields. So I want to unmarshal it in the following type:
type Foo struct { // Known fields A int `json:"a"` B int `json:"b"` // Unknown fields X map[string]interface{} `json:???` // Rest of the fields should go here. }
How do I do that?
To parse JSON, we use the Unmarshal() function in package encoding/json to unpack or decode the data from JSON to a struct. Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. Note: If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.
Unmarshal is the contrary of marshal. It allows you to convert byte data into the original data structure. In go, unmarshaling is handled by the json. Unmarshal() method.
Marshal and Unmarshal convert a string into JSON and vice versa. Encoding and decoding convert a stream into JSON and vice versa.
One option is to unmarshal twice: once into a value of type Foo
and once into a value of type map[string]interface{}
and removing the keys "a"
and "b"
:
type Foo struct { A int `json:"a"` B int `json:"b"` X map[string]interface{} `json:"-"` // Rest of the fields should go here. } func main() { s := `{"a":1, "b":2, "x":1, "y":1}` f := Foo{} if err := json.Unmarshal([]byte(s), &f); err != nil { panic(err) } if err := json.Unmarshal([]byte(s), &f.X); err != nil { panic(err) } delete(f.X, "a") delete(f.X, "b") fmt.Printf("%+v", f) }
Output (try it on the Go Playground):
{A:1 B:2 X:map[x:1 y:1]}
Another option is to unmarshal once into an map[string]interface{}
and handle the Foo.A
and Foo.B
fields manually:
type Foo struct { A int `json:"a"` B int `json:"b"` X map[string]interface{} `json:"-"` // Rest of the fields should go here. } func main() { s := `{"a":1, "b":2, "x":1, "y":1}` f := Foo{} if err := json.Unmarshal([]byte(s), &f.X); err != nil { panic(err) } if n, ok := f.X["a"].(float64); ok { f.A = int(n) } if n, ok := f.X["b"].(float64); ok { f.B = int(n) } delete(f.X, "a") delete(f.X, "b") fmt.Printf("%+v", f) }
Output is the same (Go Playground):
{A:1 B:2 X:map[x:1 y:1]}
It's not nice, but you could to it by implementing Unmarshaler
:
type _Foo Foo func (f *Foo) UnmarshalJSON(bs []byte) (err error) { foo := _Foo{} if err = json.Unmarshal(bs, &foo); err == nil { *f = Foo(foo) } m := make(map[string]interface{}) if err = json.Unmarshal(bs, &m); err == nil { delete(m, "a") delete(m, "b") f.X = m } return err }
The type _Foo
is necessary to avoid recursion while decoding.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With