Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unmarshal JSON with some known, and some unknown field names

Tags:

json

go

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?

like image 311
Abyx Avatar asked Oct 30 '15 13:10

Abyx


People also ask

How do you Unmarshal a JSON?

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.

What is the difference between Marshal and Unmarshal in Golang?

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.

What is Gollg Unmarshal marshal?

Marshal and Unmarshal convert a string into JSON and vice versa. Encoding and decoding convert a stream into JSON and vice versa.


2 Answers

Unmarshal twice

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

Unmarshal once and manual handling

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]} 
like image 154
icza Avatar answered Oct 13 '22 07:10

icza


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.

like image 42
0x434D53 Avatar answered Oct 13 '22 09:10

0x434D53