Golang JSON Unmarshal Examples

In today's post, we will walk through several scenarios to parse json files in Golang. JSON (JavaScript Object Notation) is a simple data interchange format. Syntactically it resembles the objects and lists of JavaScript. It is most commonly used for communication between web back-ends and JavaScript programs running in the browser, but it is used in many other places, too. Its home page, json.org, provides a wonderfully clear and concise definition of the standard.

With the json package it’s a snap to read and write JSON data from your Go programs using Unmarshal().

Advertisement

Package json implements encoding and decoding of JSON as defined in RFC 7159. The mapping between JSON and Go values is described in the documentation for the Marshal and Unmarshal functions.

 

Golang JSON Unmarshal() Examples

Example-1: Parse structured JSON data

func Unmarshal(data []byte, v any) error: Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.

Here's an example of parsing a json file to a struct.

json file:

{
    "name": "Harry Potter",
    "score": 9.5
  }

Code:

package main

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

type Student struct {
	Name  string  `json:"name"`
	Score float64 `json:"score"`
}

func main() {
	// open json file
	jsonFile, err := os.Open("student.json")

	// if return error, print out it
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("Successfully open student.json")
	// defer the closing of json file
	defer jsonFile.Close()
        
        // Unmarshal() function accepts []byte as param
	byteValue, _ := ioutil.ReadAll(jsonFile)
	var student Student
	err = json.Unmarshal(byteValue, &student)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", student)
}

Output:

Successfully open student.json
{Name:Harry Potter Score:9.5}

 

Example-2: Parsing Structured Complex JSON data

For example, we want to parse this json file:

Advertisement
{
    "id": "0001",
    "type": "donut",
    "name": "Cake",
    "ppu": 0.55,
    "batters": {
      "batter": [
        {
          "id": "1001",
          "type": "Regular"
        },
        {
          "id": "1002",
          "type": "Chocolate"
        },
        {
          "id": "1003",
          "type": "Blueberry"
        },
        {
          "id": "1004",
          "type": "Devil's Food"
        }
      ]
    },
    "topping": [
      {
        "id": "5001",
        "type": "None"
      },
      {
        "id": "5002",
        "type": "Glazed"
      },
      {
        "id": "5005",
        "type": "Sugar"
      },
      {
        "id": "5007",
        "type": "Powdered Sugar"
      },
      {
        "id": "5006",
        "type": "Chocolate with Sprinkles"
      },
      {
        "id": "5003",
        "type": "Chocolate"
      },
      {
        "id": "5004",
        "type": "Maple"
      }
    ]
  }

We can see that we have to parse json file to a struct which contains nested struct.

package main

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

type Cake struct {
	ID      string  `json:"id"`
	Type    string  `json:"type"`
	Name    string  `json:"name"`
	Ppu     float64 `json:"ppu"`
	Batters struct {
		Batter []Batter
	} `json:"batters"`
	Topping []struct {
		ID   string `json:"id"`
		Type string `json:"type"`
	} `json:"topping"`
}

type Batter struct {
	ID   string
	Type string
}

func main() {
	// open json file
	jsonFile, err := os.Open("complex.json")
	// if os.Open returns an error then print out it
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Successfully Opened complex.json")
	// defer the closing of the json file
	defer jsonFile.Close()

	byteValue, _ := ioutil.ReadAll(jsonFile)
	var cake Cake
	err = json.Unmarshal(byteValue, &cake)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Type: %+v\n", cake.Type)
	fmt.Printf("Name: %+v\n", cake.Name)
	fmt.Printf("Batters: %+v\n", cake.Batters)
}

Output:

Successfully Opened complex.json
Type: donut
Name: Cake
Batters: {Batter:[{ID:1001 Type:Regular} {ID:1002 Type:Chocolate} {ID:1003 Type:Blueberry} {ID:1004 Type:Devil's Food}]}

Noted that:

  • Struct's fields need to be exported (upper-case).
  • Go uses convention to determine the attribute name for mapping JSON properties. If you want a different attribute name than the one provided in JSON data, you can specify mapping to field by specify json tag. To have the JSON parser/writer skip a field, just give it the name "-". The JSON parser accepts a flag in the tag to indicate what to do if the field is empty. The omitempty flag instructs it not to include the JSON value in the output if it is the type's "zero-value." The "zero-value" for numbers is 0, the empty string for strings, and nil for maps, slices, and pointers. This is how the omitempty flag is included. For example:
type Student struct {
  Name string  `json:"fullName,omitempty"`
  Score float64 `json:"-"`
}

We can defined a nested struct instead of 2 structs like the above example:

type Cake struct {
	ID      string  `json:"id"`
	Type    string  `json:"type"`
	Name    string  `json:"name"`
	Ppu     float64 `json:"ppu"`
	Batters struct {
		Batter []struct {
			ID   string `json:"id"`
			Type string `json:"type"`
		} `json:"batter"`
	} `json:"batters"`
	Topping []struct {
		ID   string `json:"id"`
		Type string `json:"type"`
	} `json:"topping"`
}

 

Example-3: Parsing Unstructured Data

In some cases, we do not know the structure of your JSON properties beforehand, so we cannot define structs to unmarshal your data. To deal with these cases we have to create a map of strings to empty interfaces. Let's take a look at the below example:

json file:

{
	"id":123,
	"name":"GoLinux Cloud",
	"address":{
			"street":"Summer",
			"city":"San Jose",
		},
	"phoneNumber": 1234567890,
	"role":"Admin",
        "someField": "unstructed"
}

Code:

package main

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

func main() {
	// open json file
	jsonFile, err := os.Open("unstructed.json")

	// if return error, print out it
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("Successfully open unstructed.json")
	// defer the closing of json file
	defer jsonFile.Close()

	byteValue, _ := ioutil.ReadAll(jsonFile)
	var dev map[string]interface{}
	err = json.Unmarshal(byteValue, &dev)
	if err != nil {
		panic(err)
	}

	//iterate through the map
	for key, value := range dev {
		fmt.Println(key, value)
	}
}

Output:

Successfully open unstructed.json
id 123
name GoLinux Cloud
address map[city:San Jose street:Summer]
phoneNumber 1.23456789e+09
role Admin
someField unstructed

 

Summary

In this tutorial, we have show you some example of using Unmarshal() function to parse both struct and unstructured data. Go is a strongly typed language, and therefore working with JSON is always going to be tricky. Check the err parameter returned by Marshal and Unmarshal at all times. It's how you know if there is a syntax error in the JSON you're parsing. If you don't check it, your program will continue to run with the zeroed-out struct you've already created, potentially leading to unexpected behavior.

 

References

https://pkg.go.dev/encoding/json#Marshal
https://pkg.go.dev/encoding/json

 

Categories GO

Didn't find what you were looking for? Perform a quick search across GoLinuxCloud

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment

X