In this post, we'll look at how to parse a JSON string for structured and unstructured data. We also handle the situation where the json
string contains a backlash. In the previous post, I demonstrated how to parse a json file in Golang using the json package and Unmarshal()
function. In golang, we also use this package to convert a json
string to a struct. To better understand how it works, consider the following examples.
json package
: 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.
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
.
Golang parse JSON into a struct
Example-1: Parse JSON from a variable into nested struct
Here is an example of parsing a json string
to a nested struct data in Golang using the Unmarshal()
function:
package main
import (
"encoding/json"
"fmt"
)
// declaring a struct
type Squad struct {
SquadName string `json:"squadName"`
Formed int `json:"formed"`
Leader string `json:"leader"`
Status bool `json:"active"`
Members []struct {
Name string `json:"name"`
Age int `json:"age"`
SecretIdentity string `json:"secretIdentity"`
} `json:"members"`
}
// main function
func main() {
// defining a struct instance
var squad Squad
// string json
jsonString := `{
"squadName": "Go Linux Cloud",
"formed": 2022,
"leader": "",
"active": true,
"members": [
{
"name": "Anna",
"age": 30,
"secretIdentity": "Duke"
},
{
"name": "Harry Potter",
"age": 32,
"secretIdentity": "Jane"
}]}`
// decoding squad struct from json string
err := json.Unmarshal([]byte(jsonString), &squad)
if err != nil {
// print out if error is not nil
fmt.Println(err)
}
// printing details of struct
fmt.Println("Struct is:", squad)
fmt.Println("Squad's name is:", squad.SquadName)
fmt.Println("Squad's leader is:", squad.Leader)
fmt.Println("1st member is:", squad.Members[0].Name)
}
Output:
Squad's name is: Go Linux Cloud
Squad's leader is:
1st member is: Anna
Example-2: Parse JSON from a file into struct
In this example we will read JSON from a file and parse it into a struct. Here is my sample data.json
which we will use in this example:
{
"name": "Amit Kumar",
"age": 34,
"address": {
"street": "123 Some Random Street",
"city": "Chennai",
"state": "TN",
"zip": "763098"
},
"phoneNumbers": [
{
"type": "home",
"number": "0123456789"
},
{
"type": "work",
"number": "0987654321"
}
]
}
We can use the json
package to parse JSON data from a file into a struct. But we need to define the struct that matches the structure of JSON. Then we can use the json.Unmarshal
function to parse the JSON data from a file into an instance of that struct.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
Zip string `json:"zip"`
}
type PhoneNumber struct {
Type string `json:"type"`
Number string `json:"number"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
PhoneNumber []PhoneNumber `json:"phoneNumbers"`
}
func main() {
// Read the file
data, err := ioutil.ReadFile("data.json")
if err != nil {
fmt.Println(err)
return
}
// Create a struct to hold the JSON data
var person Person
// Unmarshal the JSON data into the struct
err = json.Unmarshal(data, &person)
if err != nil {
fmt.Println(err)
return
}
// Print the data
fmt.Println(person)
}
In this example, the struct Person
is defined with fields that match the structure of the JSON data. The json struct tags are used to indicate the corresponding field names in JSON.
Then, the ioutil.ReadFile
function is used to read the contents of the file "data.json
" into the data variable. Then the json.Unmarshal
function is used to parse the JSON data into an instance of struct Person.
Note that, if the structure of the JSON is not known and we want to handle all types of JSON, then we can use map[string]interface{}
instead of struct as I have shown in next examples.
Output:
# go run main.go
{Amit Kumar 34 {123 Some Random Street Chennai TN 763098} [{home 0123456789} {work 0987654321}]}
Golang parse JSON without struct into map
Example-1: Parse JSON from a variable into map
In some cases, we do not know the structure of your JSON properties beforehand, so we cannot define structs to unmarshal
our 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:
package main
import (
"encoding/json"
"fmt"
)
// main function
func main() {
// defining a map
var result map[string]interface{}
// string json
jsonString := `{
"results": [
{
"nameChunk1": [
{
"name1": "Anna",
"name2": "Bob"
}
],
"nameChunk2": [
{
"name1": "Clay",
"name2": "Dean"
}
]
}
],
"author": "Go Linux Cloud"
}`
err := json.Unmarshal([]byte(jsonString), &result)
if err != nil {
// print out if error is not nil
fmt.Println(err)
}
// printing details of map
// iterate through the map
for key, value := range result {
fmt.Println(key, ":", value)
}
}
Output:
results : [map[nameChunk1:[map[name1:Anna name2:Bob]] nameChunk2:[map[name1:Clay name2:Dean]]]]
author : Go Linux Cloud
Parse content of JSON file into map
In the previous example we parsed JSON from a variable, now in this example we have a JSON file data.json
with following content:
{
"name": "Amit Kumar",
"age": 34,
"address": {
"street": "123 Some Random Street",
"city": "Chennai",
"state": "TN",
"zip": "763098"
},
"phoneNumbers": [
{
"type": "home",
"number": "0123456789"
},
{
"type": "work",
"number": "0987654321"
}
]
}
Here is a simple golang program to load data.json
file and convert it into map:
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
// Open the file
file, err := os.Open("data.json")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
// Create a map to hold the JSON data
var data map[string]interface{}
// Decode the JSON data into the map
err = json.NewDecoder(file).Decode(&data)
if err != nil {
fmt.Println(err)
return
}
// Print the data
fmt.Println(data)
}
This example uses the os.Open
function to open the file "data.json
" and the json.NewDecoder
function to read the JSON data from the file. The json.Decode
method is used to parse the JSON data into the map data.
Note that the data variable is defined as map[string]interface{}
. This is because the json.Decode
function returns a map with keys of type string and values of type interface{}
, which can hold any JSON value.
Output:
# go run main.go
map[address:map[city:Chennai state:TN street:123 Some Random Street zip:763098] age:34 name:Amit Kumar phoneNumbers:[map[number:0123456789 type:home] map[number:0987654321 type:work]]]
Now we used os.Open
to read the file but you can also use ioutil.Read()
to perform the same as shown below:
// Read the file
data, err := ioutil.ReadFile("data.json")
if err != nil {
fmt.Println(err)
return
}
Golang parse JSON string with backslash
Using strconv.Unquote() function
func Unquote(s string) (string, error)
: Unquote interprets s as a single-quoted, double-quoted, or backquoted Go string literal, returning the string value that s quotes. (If s is single-quoted, it would be a Go character literal; Unquote returns the corresponding one-character string.)
Here is an example of parsing this string "{\"channel\":\"buu\",\"sender\":\"anna\", \"receiver\":\"bob\", \"message\":\"hello\"}"
to a string map.
package main
import (
"encoding/json"
"fmt"
"strconv"
)
// main function
func main() {
// defining a map
var result map[string]interface{}
// string json
jsonString := `"{\"channel\":\"buu\",\"sender\":\"anna\", \"receiver\":\"bob\", \"message\":\"hello\"}"`
// replace \ with empty character
s, _ := strconv.Unquote(string(jsonString))
err := json.Unmarshal([]byte(s), &result)
if err != nil {
// print out if error is not nil
fmt.Println(err)
}
// printing details of map
// iterate through the map
for key, value := range result {
fmt.Println(key, ":", value)
}
}
Output when we iterate through the map:
sender : anna
receiver : bob
message : hello
channel : buu
Using Replace() function
func Replace(s, old, new string, n int) string
: Replace returns a copy of the string s with the first n non-overlapping instances of old replaced by new
In this example, we will replace backslash with empty character and then parse it like a normal json string:
package main
import (
"encoding/json"
"fmt"
"strings"
)
// main function
func main() {
// defining a map
var result map[string]interface{}
// string json
jsonString := `"{\"channel\":\"buu\",\"sender\":\"anna\", \"receiver\":\"bob\", \"message\":\"hello\"}"`
jsonString = strings.Replace(jsonString, "\\", "", -1)
jsonString = jsonString[1 : len(jsonString)-1]
err := json.Unmarshal([]byte(jsonString), &result)
if err != nil {
// print out if error is not nil
fmt.Println(err)
}
// printing details of map
// iterate through the map
for key, value := range result {
fmt.Println(key, ":", value)
}
}
The output will be the same with example 3.
Summary
The above examples demonstrate how to read a json string
using the golang Unmarshal()
function. If your json string
contains a backslash, you can use Replace()
or Unquote()
function.
References
https://www.json.org/json-en.html
https://pkg.go.dev/encoding/json
https://pkg.go.dev/strings#Replace