YWCA Hello
YWCA Hello

Reputation: 3059

How to not unmarsal nested JSON in Golang

I have some JSON that I wish to unmarshal in Go. One of the top-level keys of the JSON dictionary has a value that is also parsable JSON. For example:

{ 
    "Name": "Tony",
    "Age": 50,
    "Extra": {\"Weight\":180}
}

I have a corresponding struct that I want to unmarsal to:

type Person struct {
    Name string
    Age int
    Extra []byte
 }

I want the Extra key to be a byte array because the JSON structure will vary and the structure of it is not important to the program being written. The program simply needs to push this data along down the line as a byte array.

How can I coax the Go JSON encoder to handle the value of extra like this?

Upvotes: 0

Views: 183

Answers (2)

Thundercat
Thundercat

Reputation: 120999

Use json.RawMessage or interface{}:

type Person struct {
    Name string
    Age int
    Extra json.RawMessage
}

type Person struct {
    Name string
    Age int
    Extra interface{}
}

With a json.RawMessage, the data is pushed down the line as a slice of bytes. With an interface{}, the data is unmarshaled to Go values (bool, float64, string, []interface{}, map[string]interface{}, nil) and marshaled back to JSON. Both options pass arbitrary JSON through the application. If the application does not have a reason to use one or the other, then json.RawMessage is probably the better choice because it's faster.

Upvotes: 0

damoiser
damoiser

Reputation: 6238

You have two options:

Using json.RawMessage

Here you have to slightly change your struct:

type Person struct {
    Name string
    Age int
    Extra *json.RawMessage
}

then, you can Unmarshal as usual

person := Person{}
err := json.Unmarshal(raw_bytes, &person)
// handle err

finally, you can Unmarshal Extra again, you can even do some assertion type on it to previously detect the type

var extra_option_1 string
extra_option_2 := AnotherStruct{}

err := json.Unmarshal(raw_bytes, &extra_option_1)
// handle err
    
// or
    
err := json.Unmarshal(raw_bytes, &extra_option_2)
// handle err

 Using map[string]interface{}{} - honestly not so useful in your case

You can directly unmarshal "unknown" or dynamics payloads using a general map[string]interface{}{}.
BUT I discourage this usage because is slower and you should always know what to expect.

Anyway, hereafter is a pseudo code:

payload := map[string]interface{}{}
err := json.Unmarshal(raw_bytes, &payload)
// handle err

In this last case, you need even to do assertion to each field.

Upvotes: 1

Related Questions