Project 12: JSON parser

Nemuel Wainaina
4 min readOct 1, 2024

--

Complete video course: Udemy

Introduction

A JSON parser is a program that reads data formatted in the JSON (JavaScript Object Notation) format and converts it into a structure that can then be easily used in code. JSON is a way of writing data, a JSON data entry is usually a set of one or more key-value pairs enclosed in curly braces eg.

{
"name": "Alice",
"age": 30
}

Our project will involve building a program that takes in a file containing some JSON data (similar to the above snippet), parses it, and then displays the name and age values to the user.

Practical

Boilerplate code:

package main

import (

)

func main() {

}

Let us first import the packages that we will need:

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

The encoding/json package is what we will use for the parsing, fmt for the usual input/output operations, and os for opening and reading from the user-supplied file. Until now, we have been prompting the user for input after the program has been executed. Another way to get data from the user for use within a program is by command-line arguments. Command-line arguments are inputs passed to a program when it is run from the command line and usually come after the program name. Besides serving as inputs to the program, they can be used to modify its behavior.

In our case, we intend for our program to be run as follows:

./jsonparser <json_file eg. user.json>

jsonparser is the presumed name of the program, and after it, we have a JSON file to be parsed. For simplicity, create a new file called user.json in the same directory and add the following:

{
"name": "Alice",
"age": 30
}

In the main function of our Go file, we will start by confirming that the user has provided the required command-line argument (the JSON file). By default, the name of the program itself is counted as the first argument, so with the JSON file argument, the arguments count will be 2. For this, we use os.Args which is a collection of all arguments (starting with the program name):

if len(os.Args) != 2 {
fmt.Println("Syntax: ./parser <json_file>")
return
}

In the above snippet, we check whether there are 2 arguments (program name and JSON file to be parsed), and if not, print an error message and return from the program.

The next step is to read from the file. The file name is at index 1 of the os.Args collection:

json_file := os.Args[1]

Reading the file’s contents:

contents, err := os.ReadFile(json_file)
if err != nil {
fmt.Println("Error reading from file")
return
}

Note that the ReadFile also returns an error value if any occurs so we can use it for error handling.

The next step is the actual parsing, and to do it, we first need to define a struct matching the JSON data we wish to parse. A struct is a composite data type that allows us to combine variables of different types into a single type. In our case, for example, we have the name which is a string, and the age which is an int:

type User struct {
Name string
Age int
}

We use the type & struct keywords and wrap the variable declarations in curly braces. Please note that this declaration is being done outside the main function.

Back in the main function, we can now create an instance of our User struct:

var user User

To parse the file's contents into the user variable declared above, we use the Unmarshal function of the json package. This function takes in 2 values: the data to be parsed, and the memory location of the structure to hold the result. It returns an error if any occurs, and that will help us know if the parsing has been successful:

err = json.Unmarshal(contents, &user)
if err != nil {
fmt.Println(err)
return
} else {
fmt.Printf("Name: %s\n", user.Name)
fmt.Printf("Age: %d\n", user.Age)
}

Note that to access the fields in a struct, we use the dot notation: struct_instance_name.field_name eg. user.Name

Complete code:

package main

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

type User struct {
Name string
Age int
}

func main() {
if len(os.Args) != 2 {
fmt.Println("Syntax: ./parser <json_file>")
return
}

json_file := os.Args[1]
contents, err := os.ReadFile(json_file)
if err != nil {
fmt.Println("Error reading from file")
return
}

var user User
err = json.Unmarshal(contents, &user)
if err != nil {
fmt.Println(err)
return
} else {
fmt.Printf("Name: %s\n", user.Name)
fmt.Printf("Age: %d\n", user.Age)
}
}

Next: Project 13: File integrity checker

--

--

Nemuel Wainaina
Nemuel Wainaina

Written by Nemuel Wainaina

Security Researcher | Software Engineer

No responses yet