The Problem

I want to use response where the content field comes from external API response which will return varied formats. For example:

{
    "souce": "http://guesthost.com/check-in",
    "payload": {
        "token" : "somerandomtoken",
        "id" : "1up"
    },
    "content": {
            "name": "John Doe",
            "occupation": "Doctor"
    }
}

or

{
    "souce": "http://localhost.com/hello-world",
    "payload": {
        "token" : "somerandomtoken",
    },
    "content": {
        "status": 404,
        "message": "Missing resource or wrong naming"
    }
}

If you come here you should know that the usual way for Golang to create JSON is using struct then convert it to JSON string using json.Marshal(). But the downside is we need to create every Struct for each JSON if we want to represent all those formats. Like my current problem.

ex.

type ReturnStruct struct{
    Source string `json:"source"`
    Payload RequestPayload `json:"payload"`
    Content string `json:"content"`
}

this will return the content field on JSON as string. Which in turn will escape the JSON string response from 3rd party. Having a string return instead of JSON is little bit pain especially when we want to process the data later as this kind of data will be stored to database(usually) and the data processor can be external app that reads JSON.

What can I do?

  • Using map is out of question. We could make map[string]interface{} but the output will become an array instead of JSON.
  • Creating each struct for each content is a no go too. If possible we could just automate it.

The answer

Introducing json.RawMessage.

We only need to specify the type of the field to *json.RawMessage to make the JSON string is retained as JSON object instead of other types(ex. string) when we json.Marshal() it

import (
	"encoding/json"
	"fmt"
	"os"
)

func main() {
    // Read JSON string
	h := json.RawMessage(`{"precomputed": true}`)

    // Declare the main JSON structure
    // NOTICE the header type and how its value is coming from
	c := struct {
		Header *json.RawMessage `json:"header"`
		Body   string           `json:"body"`
	}{Header: &h, Body: "Hello Gophers!"}

	b, err := json.MarshalIndent(&c, "", "\t")
	if err != nil {
		fmt.Println("error:", err)
	}
	os.Stdout.Write(b)

}

Looking by the example above. If we want to change the header value. We could do it in the first line of the main() function which is string, not a struct. This is the flexibility of json.RawMessage that breaks the assumption that creating dynamic JSON in golang is hard.

Conclusion

This is a great feature to compose dynamic JSON for your app and/or storage using Golang.