Marshaling & Unmarshaling in Go

What is Marshaling and Unmarshaling in Go?

The Go programming language, or Golang is a statically typed, compiled language created by Google and appeared first back in 2009.
It is C-like syntactically but with structural typing, garbage collection, and memory safety thrown in for good measure.

It has become extremely popular in recent years with it being quite developer friendly and easy to pick up. Go’s main
usages have been in the cloud platform development, CLI programs, and web development space.

Using Go, or most languages, you’re bound to run into and will work with JSON, especially in the web dev space. This
type of work can consist of reading JSON data from an API response and having to parse it for your web application, or
even write our own JSON files from Go structs created ourselves.

“encoding/json” package

The encoding/json package is part of the Go standard library which allows us to handle JSON data efficiently. There’s a
good chance it’ll be one of the more popular packages you use within the Go standard library.

The package provides us with API functions that allow us to encode and decode JSON objects to Go structs and
vice versa. These processes are what we call marshaling and unmarshaling.

You can read the full package documentation here


Marshaling

Marshaling is the idea and practice of encoding a Go struct to a JSON object. The Marshal function allows us to do
this conversion and the function signature is as follows:

		func Marshal(v interface{}) ([]byte, error)

From the function definition we see that we can essentially pass any type to be encoded and we’ll be returned a byte
slice, along with an error should one have happened along the way.

Marshaling Basic Objects

  1		package main
  2
  3
  4 		import (
  5			"fmt"
  6			"encoding/json"
  7		)
  8
  9		func main() {
 10			testScores := map[string]int {
 11				"James": 67,
 12				"Tyler": 49,
 13				"Amanda": 88,
 14				"Brendan": 52,
 15		}
 16
 17		bytes, _ := json.Marshal(testScores)
 18		fmt.Println(string(bytes))
 19 		}

Output

		ryan@pallas:~/dev/marshal$ go run main.go 
		{"Amanda":88,"Brendan":52,"James":67,"Tyler":49}

So what’s going on in our simple example? All we’ve done is take a map data structure called testScores and encoded
it via the json.Marshal() function before printing the result which has been converted to a string. Pretty simple stuff right?

Structs can also be encoded to JSON like so:

  1		package main
  2 
  3		import (
  4			"fmt"
  5			"encoding/json"
  6 		)
  7 
  8 		type Car struct { 
  9   			Make    string
 10   			Model   string
 11   			Year    int
 12 		}
 13 
 14 		func main() {
 15   			myCar := Car{"Audi", "A4", 2018}
 16         
 17   			bytes, _ := json.Marshal(myCar)     
 18   			fmt.Println(string(bytes))
 19 		}
Output
		ryan@pallas:~/dev/marshal$ go run main.go 
		{"Make":"Audi","Model":"A4","Year":2018}

Note: The field names in the struct are capatalized which means it’s exportable. Otherwise the field won’t be
included in the encoding process.

Marshaling complex objects

Hopefully the previous marshaling examples were easy to follow, I’d recommend playing around with the examples
yourself, but let’s take it up a notch.

The previous examples were solid, and they work, but they’re not entirely realistic. Most of the time when working with
real-world programs there’s a good chance our data will be a lot more complex, so let’s try marshaling
something a little more conceivable.

  1		package main
  2 
  3 		import (
  4         		"fmt"
  5         		"encoding/json"
  6 		)
  7 
  8 		type Driver struct {
  9         		Id int `json:"id"`
 10         		Name string `json:"name"`
 11         		Country string `json:"country"`
 12 		}
 13  
 14 		type Team struct { 
 15         		Id int `json:"id"`
 16         		Name string `json:"name"`
 17         		Driver Driver `json:"driver"`       
 18         		Championships int `json:"championships"`
 19 		}
 20 
 21 		func main() {
 22         		f1Team := Team {
 23                 		Id: 35168,
 24                 		Name: "Mercedes AMG Petronas",
 25                 		Driver: Driver {248, "Lewis Hamilton", "UK"},
 26                 		Championships: 8,
 27         	}
 28         
 29         	bytes, _ := json.MarshalIndent(f1Team, "", "\t")
 30         	fmt.Println(string(bytes))
 31 		}

Alright, there’s a couple of things going on here that we haven’t seen yet. What we’ve done in the code above is initialize
an F1 team with information on a driver, and the team itself.

Notice the tags after the struct fields. This allows us to rename the fields once they’re marshaled, in this case all I’m doing
is making them lowercase.

The other noticeable difference is the function we use to marshal our data, it’s json.MarshalIndent() instead
of what we used previously which was json.Marshal().

As our marshaling becomes more complex, so does what comes out the other side, the
indent variant does just that, it indents and formats the JSON for us.

Output
		ryan@pallas:~/dev/marshal$ go run main.go 
		{
			"id": 35168,
			"name": "Mercedes AMG Petronas",
			"driver": {
				"id": 248,
				"name": "Lewis Hamilton",
				"country": "UK"
			},
			"championships": 8
		}

We also have the ability to ignore certain fields as well as ignore empty fields, this is one you can probably test yourself, take
the code above and replace line 10 with:

 10		Name string `json:"-"`

And line 18 with:

 18		Championships int `json:"championships,omitempty"`

While deleting line 26.

If done correctly, you’ll have discarded the name of the driver, while also discarding the amount of championships
because none were passed.

Output
		ryan@pallas:~/dev/marshal$ go run main.go 
		{
			"id": 35168,
			"name": "Mercedes AMG Petronas",
			"driver": {
				"id": 248,
				"UK": "UK"
			}
		}

Unmarshaling

The opposite of marshaling is of course, unmarshaling. The unmarshal function gives us the ability to decode our JSON data
and the function signature is as follows:

		func Unmarshal(data []byte, v interface{}) error

As you can see, the function accepts two parameters. These being data, which is the JSON content, and and empty interface
reference v. The function doesn’t create or return Go objects so a reference must be passed to store the decoded content.

Unmarshaling basic objects

  1		package main
  2 
  3		import( 
  4			"fmt"
  5         		"encoding/json"
  6 		)
  7 
  8 		type License struct { 
  9         		Name string `json:"name"` 
 10         		Age int `json:"age"`
 11         		State string `json:"state"`
 12 		}
 13 
 14 		func main() {
 15         		jsonInput := `{  
 16                 		"name": "John Doe",
 17                 		"age": 43,
 18                 		"state": "CA"
 19         		}`
 20  
 21         	var license License
 22  
 23         	err := json.Unmarshal([]byte(jsonInput), &license)
 24         	if err != nil { 
 25             	fmt.Println("Error")
 26                 	return
 27         	}
 28  
 29         	fmt.Println(license) 
 30 		}
Output
	ryan@pallas:~/dev/unmarshal$ go run main.go 
	{John Doe 43 CA}

In the example above we’re taking JSON data passed to us and unmarshaling it to our license struct.

You can see we declare a license variable of the same type which will be used as the reference needed
to hold the decoded content like we mentioned previously.

We must also pass the JSON input as a bytes slice first, we cannot pass a multi-line string.

Unmarshaling complex objects

Again, the previous is quite simple and somewhat unrealistic, so lets look at something a little more deeper.

  1		package main
  2
  3	 	import( 
  4         		"fmt"
  5         		"encoding/json"
  6 		)
  7    
  8 		type Car struct {     
  9         		Id int `json:"id"`        
 10         		Name string `json:"name"`
 11         		Dealership struct { 
 12                 		Id int `json:"id"`
 13                 		Name string `json:"name"`
 14                 		Location string `json:"location"`
 15         		} `json:"dealership"`
 16         		Price int `json:"price"`
 17 		}
 18  
 19 		func main() {
 20         		jsonInput := `[
 21         		{         
 22                 		"id":35871,
 23                 		"name":"Nissan",
 24                 		"dealership":{ 
 25                         		"id":785,
 26                         		"name":"Jim's Cars",
 27                         		"location":"Atlanta"
 28                 		},
 29                 		"price":17500
 30         		},
 31         		{ 
 32                 		"id":16287,
 33                 		"name":"Audi",  
 34                 		"dealership":{
 35                         		"id":176,
 36                         		"name":"Automart",  
 37                         		"location":"Newark" 
 38                 	},
 39                 		"price":23000
 40         		}]
 41         		` 
 42 
 43         		var cars []Car     
 44  
 45         		err := json.Unmarshal([]byte(jsonInput), &cars)   
 46         		if err != nil {
 47                 		fmt.Println("Error")
 48                 		return
 49         		}
 50          
 51         		fmt.Println(cars)   
 52 		}

Output

	ryan@pallas:~/dev/unmarshal$ go run main.go 
	[{35871 Nissan {785 Jim's Cars Atlanta} 17500} {16287 Audi {176 Automart Newark} 23000}]

Our code above takes some nested JSON and unmarshals it into a Go struct, it’s not too dissimilar to the
first unmarshal example but it’s important to understand how to work with nested data.

Summary & Conclusion

We’ve gone over what both marshaling and unmarshaling is in Go and how the practices can
be implemented and applied.

I’d always suggest playing around with the examples to really nail what information you’re taking in.

The next step is create your own custom JSON marshal and unmarshal functions. Keep an eye out, I’ll
be releasing an article about just that very soon.