Tangible Bytes

A Web Developer’s Blog

Unstructured JSON to Golang

I really like the way Go imports json into its own data structures.

You just define a struct, annotate it to map the JSON field names to the struct field names (taking care to follow the Go convention of using Capitalised initials where the field is public).

There are also some great tools to automatically generate the struct from JSON

eg Convert JSON to Go struct

But where I got stuck was with JSON that is flexible and doesn’t match something I can directly make in Go.

In my case there is a structure - but it’s not 100% compatible with Go strong typing.

I have a large struct and some of the fields can be of more than one possible type.

The solution for me was Using Go’s json.RawMessage

Part of my struct looks as below

type Article struct {
	Entity struct {
		ID         int    `json:"id"`
		Title      string `json:"title"`
		Slug       string `json:"slug"`
		Content    string `json:"content"`
		Components []struct {
			Type string          `json:"type"`
			Data json.RawMessage `json:"data"`
		} `json:"components"`

To get my struct from a file

func getArticleFromFile(filename string) Article {
	jsonFile, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	articleBytes, err := io.ReadAll(jsonFile)
	if err != nil {
		panic(err)
	}
	var article Article
	json.Unmarshal(articleBytes, &article)
	return article
}

So here I have the whole object imported - but the variable bit is still raw - a byte array - that I can unmarshal later when I have figured out what kind of object it is.

My components array has a “type” field which tells me what kid if thing it contains - so I can use a switch statement here

Im my case I have a type called “references” which is just an array of strings and can be unmarshalled like this

	for _, component := range article.Entity.Components {
		switch component.Type {
		case "references":
			var data []string
			json.Unmarshal(component.Data, &data)

Other types are more complex and I’ll need to create new structs for these.

In my case I don’t need to fully resolve the whole JSON object into a single structure - I just need to be able to parse it and use the data - having a few different objects that represent the whole thing is no problem.