Nikola Stanković
Nikola Stanković

Reputation: 881

Unmarshal JSON to struct

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

Answers (2)

kostix
kostix

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

Jeyem
Jeyem

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

Related Questions