Reputation: 881
How can I unmarshal a JSON string into a struct, when the struct type is given in the JSON string. This is my code:
package main
import (
"fmt"
"encoding/json"
)
type ServiceResult struct {
Type string `json:"type"`
Content interface{} `json:"content"`
}
type Person struct {
Name string `json:"name"`
}
func main() {
nikola := ServiceResult{}
nikola.Type = "Person"
nikola.Content = Person{"Nikola"}
js, _ := json.Marshal(nikola)
fmt.Println("Marshalled object: " + string(js))
}
And now I want to create from this JSON string a new person but the type has to be read out of the JSON string.
{"type":"Person","content":{"name":"Nikola"}}
Upvotes: 2
Views: 6317
Reputation: 55453
First, for your type
type ServiceResult struct {
Type string `json:"type"`
Content interface{} `json:"content"`
}
you'd need to implement "custom JSON unmarshaler" by defining a method:
func (sr *ServiceResult) UnmarshalJSON(b []byte) error
to make your type satisfy the encoding/json.Unmarshaler
interface —
this will make the JSON decoder call that method when unmarshaling to
the value of that type.
In that method, you can use a helper type
type typedObject struct {
Type string `json:"type"`
Content json.RawMessage `json:"content"`
}
to first unmarshal that b
slice into it.
If unmarshaling completed w/o generating an error, your value
of type typedObject
would have the string describing the type
in its Type
string and the raw (unparsed) JSON string contained
in the "content" field in its Content
field.
You then do a switch
(or a map lookup or whatever) to perform
pick the actual Go type to unmarshal whatever data is in the Content
field into it, like in:
var value interface{}
switch sr.Type {
case "person":
value = new(Person)
case "car":
value = new(Car)
}
err = json.Unmarshal(sr.Content, value)
…where those Person
and Car
are concrete struct types
suitably armed for consumption by encoding/json
.
Please read this and this to gain full understanding of these concepts.
Upvotes: 3
Reputation: 310
when you create instance from ServiceResult you could init Content with person too then unmarshal it:
service := ServiceResult{Content: Person{}}
json.Unmarshal(data, &service)
Upvotes: -2