other resources:
- http server sample converting and responding in JSON
- http client and http server interacting through JSON
- decode string JSON to int
json: cannot unmarshal string into Go struct field Item.item_id of type int
Table of contents
Marshalling and Unmarshalling
What exactly is marshalling and unmarshalling? In simple terms:
- Marshalling is the process of converting a Go data structure into a format that can be stored or transmitted, like JSON or XML.
- Unmarshalling is the reverse - taking data in a format like JSON and converting it back into a Go data structure.
It’s kinda like packing luggage for a trip (marshalling) and then unpacking when you get home (unmarshalling). Isn’t?
👇 a simple sample...
This is where marshalling comes in handy!
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{"rahul", 12}
jsondata, _ := json.Marshal(p)
fmt.Println(string(jsondata)) // Json Data:=> {"Name":"rahul","Age":12}
fmt.Println(jsondata) // Marshal Data:=> [123 34 78 97 109 101 34 58 34 114 97 104 117 108 34 44 34 65 103 101 34 58 49 50 125]
}
Unmarshalling is equally easy
var unmarshalledP Person
json.Unmarshal(jsonData, &unmarshalledP)
fmt.Println(unmarshalledP)
Marshalling and unmarshalling use the concept of encoders and decoders in Go. An encoder writes structured Go data into a standard format like JSON. A decoder reads data in a standard format and converts it back into Go data structures.
Pretty Printing JSON Structs in Go
package main
import (
"fmt"
"encoding/json"
)
type Flight struct {
Origin string `json:"origin"`
Destination string `json:"destination"`
Price int `json:"price"`
}
func main() {
flight := Flight{
Origin: "GLA",
Destination: "JFK",
Price: 2000,
}
b, err := json.MarshalIndent(flight, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Print(string(b))
}
Nested map of map
package main
import (
"encoding/json"
"fmt"
)
func main() {
mapA := map[string]string{
"63": "Abalone",
"48": "Açaí berries",
"49": "Açaí juice",
"42": "Acorn squash",
"50": "Adzuki bean paste",
"51": "Adzuki beans",
"46": "Aged Japanese kurozu",
}
mapB := map[string]string{
"63": "Bacon",
"48": "Bananas",
"49": "Beef",
"42": "Black tea",
"50": "Blackberries",
"51": "Blueberries",
"46": "Butter",
}
mapOfMap := map[string]map[string]string{}
for id, foodA := range mapA {
mapOfMap[id] = map[string]string{}
mapOfMap[id]["A"] = foodA
mapOfMap[id]["B"] = mapB[id]
}
/*
for id, foodB := range mapB {
mapOfMap[id]["B"] = foodB
}
*/
fmt.Println(mapOfMap)
json, err := json.MarshalIndent(mapOfMap, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Println(string(json))
fmt.Println(mapOfMap["46"]["A"])
fmt.Println(mapOfMap["46"]["B"])
}
Converting map to struct
One way is to roundtrip it through JSON
package main
import (
"bytes"
"encoding/json"
)
func transcode(in, out interface{}) {
buf := new(bytes.Buffer)
json.NewEncoder(buf).Encode(in)
json.NewDecoder(buf).Decode(out)
}
Example:
package main
import "fmt"
type myStruct struct {
Name string
Age int64
}
func main() {
myData := map[string]interface{}{
"Name": "Tony",
"Age": 23,
}
var result myStruct
transcode(myData, &result)
fmt.Printf("%+v\n", result) // {Name:Tony Age:23}
}
Conversion by marshalling and unmarshalling
example to convert map to struct by marshalling and unmarshalling
map[string]interface{} to struct
package main
import (
"encoding/json"
"fmt"
)
func main() {
// map data
mapData := map[string]interface{}{
"Name": "noknow",
"Age": 2,
"Admin": true,
"Hobbies": []string{"IT","Travel"},
"Address": map[string]interface{}{
"PostalCode": 1111,
"Country": "Japan",
},
"Null": nil,
}
// struct - Need to be defined according to the above map data.
type Addr struct {
PostalCode int
Country string
}
type Me struct {
Name string
Age int
Admin bool
Hobbies []string
Address Addr
Null interface{}
}
// Convert map to json string
jsonStr, err := json.Marshal(mapData)
if err != nil {
fmt.Println(err)
}
// Convert json string to struct
var me Me
if err := json.Unmarshal(jsonStr, &me); err != nil {
fmt.Println(err)
}
// Output
fmt.Printf("Name: %s
Age: %d
Admin: %t
Hobbies: %v
Address: %v
Null: %v
", me.Name, me.Age, me.Admin, me.Hobbies, me.Address, me.Null)
}
struct to map[string]interface{}
package main
import (
"encoding/json"
"fmt"
)
func main() {
// struct data
type Addr struct {
PostalCode int
Country string
}
type Me struct {
Name string
Age int
Admin bool
Hobbies []string
Address Addr
Null interface{}
}
addr := Addr{
PostalCode: 1111,
Country: "Japan",
}
me := Me{
Name: "noknow",
Age: 2,
Admin: true,
Hobbies: []string{"IT","Travel"},
Address: addr,
Null: nil,
}
// Convert map to json string
jsonStr, err := json.Marshal(me)
if err != nil {
fmt.Println(err)
}
// Convert struct
var mapData map[string]interface{}
if err := json.Unmarshal(jsonStr, &mapData); err != nil {
fmt.Println(err)
}
// Output
fmt.Printf("Name: %v (%T)
Age: %v (%T)
Admin: %v (%T)
Hobbies: %v (%T)
Address: %v (%T)
Null: %v (%T)
", mapData["Name"], mapData["Name"], mapData["Age"], mapData["Age"], mapData["Admin"], mapData["Admin"], mapData["Hobbies"], mapData["Hobbies"], mapData["Address"], mapData["Address"], mapData["Null"], mapData["Null"])
}
other alternatives to convert map to struct
- via library:
- https://github.com/mitchellh/mapstructure
- Go library for decoding generic map values into native Go structures and vice versa.
-
import "github.com/mitchellh/mapstructure" // Hashicorp's https://github.com/mitchellh/mapstructure library does this out of the box: // the second result parameter has to be an address of the struct. mapstructure.Decode(myData, &result)
- https://github.com/mitchellh/mapstructure