JSON singkatan dari JavaScript Object Notation, merupakan stuktur format data yang bentuknya seperti Object di JavaScript. JSON merupakan struktur format data yang paling banyak digunakan saat kita membuat RESTful API.
Golang sudah menyediakan package JSON, dimana kita bisa menggunakan package ini untuk melakukan konversi data ke JSON (encode) atau sebaliknya (decode). Untuk detailnya dapat dilihat pada laman https://pkg.go.dev/encoding/json
Encode
Golang telah menyediakan function untuk melakukan konversi data ke JSON, yaitu menggunakan function json.Marshal(interface{})
. Karena parameter nya adalah interface{}
, maka kita bisa memasukkan tipe data apapun ke dalam function Marshal
.
Buatlah project baru, dan di dalamnya buat file dengan nama encode_json_test.go
dan masukkan baris kode berikut
package belajar_golang_test
import (
"encoding/json"
"fmt"
"testing"
)
func logJson(data interface{}) {
bytes, err := json.Marshal(data)
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}
func TestEncode(t *testing.T) {
logJson("Rendy")
logJson(22)
logJson(false)
logJson([]string{"Rendy", "Wijaya"})
}

JSON Object
Sebelumnya kita telah mencoba melakukan ecode data seperti string, number, boolean, dan tipe data primitif lainnya. Walaupun memang bisa dilakukan, karena sesuai dengan kontrak interface{}
, namun tidak sesuai dengan kontrak JSON, jika mengikuti kontrak json.org, data JSON bentuknya adalah Object dan Array.
JSON Object di golang direpresentasikan dengan tipe data Struct, dimana tiap attribute di JSON Object merupakan attribute di Struct.
Buat file baru dengan nama json_object_test.go
dan masukkan abis kode berikut
package belajar_golang
import (
"encoding/json"
"fmt"
"testing"
)
type Customer struct {
FirstName, LastName string
Age int
}
func TestJSONObject(t *testing.T) {
customer := Customer{
FirstName: "Rendy",
LastName: "Wijaya",
Age: 22,
}
bytes, err := json.Marshal(customer)
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}

Decode
Untuk melakukan konversi dari JSON ke tipe data di golang (Decode), kita bisa menggunakan function json.Unmarshal(byte[], interface{})
. Dimana byte[]
adalah data JSON nya, sedangkan interface{}
adalah tempat menyimpan hasil konversi, biasanya berupa pointer.
Untuk mencobanya, buat kembali file baru dengan nama decode_json_test.go
dan amsukkan baris kode berikut
package belajar_golang
import (
"encoding/json"
"fmt"
"testing"
)
func TestDecodeJSON(t *testing.T) {
jsonString := `{"FirstName":"Rendy","LastName":"Wijaya","Age":22}`
jsonBytes := []byte(jsonString)
customer := &Customer{}
err := json.Unmarshal(jsonBytes, customer)
if err != nil {
panic(err)
}
fmt.Println(customer)
fmt.Println(customer.FirstName)
fmt.Println(customer.LastName)
fmt.Println(customer.Age)
}

JSON Array
Selain tipe data dalam bentuk Object, biasanya dalam JSON, kita kadang menggunakan tipe data Array, Array di JSON mirip dengan Array di JavaScript, dia bisa berisikan tipe data primitif, atau tipe data kompleks(Object atau Array). Di golang, JSON Array di representasikan dalam bentuk Slice. Konversi dari JSON atau ke JSON dilakukan secara otomatis, oleh package JSON menggunakan tipe data Slice.
Untuk mencobanya, pertama ubah Struct Customer yang sudah kita buat sebelumnya menjadi seperti berikut
type Customer struct {
FirstName, LastName string
Age int
Hobbies []string
}
Selanjutnya buat juga file baru dengan nama json_array_test.go
dan masukkan baris kode
package belajar_golang
import (
"encoding/json"
"fmt"
"testing"
)
func TestJSONArrayEncode(t *testing.T) {
customer := Customer{
FirstName: "Rendy",
LastName: "Wijaya",
Age: 22,
Hobbies: []string{"Gaming", "Reading", "Coding"},
}
bytes, err := json.Marshal(customer)
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}
func TestJSONArrayDecode(t *testing.T) {
jsonString := `{"FirstName":"Rendy","LastName":"Wijaya","Age":22,"Hobbies":["Gaming","Reading","Coding"]}`
jsonBytes := []byte(jsonString)
customer := &Customer{}
err := json.Unmarshal(jsonBytes, customer)
if err != nil {
panic(err)
}
fmt.Println(customer)
fmt.Println(customer.FirstName)
fmt.Println(customer.LastName)
fmt.Println(customer.Age)
fmt.Println(customer.Hobbies)
}

Contoh diatas adalah cara kita melakukan encode dan decode JSON Array sederhana, nah bagaimana jika JSON nya complex, maka cara nya adalah seperti berikut
Peratma ubah terlebih dahulu Struct Customer dan kita juga akan membuat Struct baru dengan nama Address, kemudian kita masukkan ke Struct customer sebagai Slice of Struct agar Customer bisa membuat banyak Address.
type Address struct {
Street, Country, PostalCode string
}
type Customer struct {
FirstName, LastName string
Age int
Hobbies []string
Address []Address
}
Selanjutnya untuk baris kode decode dan encode json complex adalah seperti berikut
package belajar_golang
import (
"encoding/json"
"fmt"
"testing"
)
func TestJSONArrayComplexEncode(t *testing.T) {
customer := Customer{
FirstName: "Rendy",
Address: []Address{
{
Street: "Jalan Belum Ada",
Country: "Indonesia",
PostalCode: "9999",
},
{
Street: "Jalan Lagi DIbangun",
Country: "Indonesia",
PostalCode: "8888",
},
},
}
bytes, err := json.Marshal(customer)
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}
func TestJSONArrayComplexDecode(t *testing.T) {
jsonString := `{"FirstName":"Rendy","LastName":"","Age":0,"Hobbies":null,"Address":[{"Street":"Jalan Belum Ada","Country":"Indonesia","PostalCode":"9999"},{"Street":"Jalan Lagi DIbangun","Country":"Indonesia","PostalCode":"8888"}]}`
jsonByte := []byte(jsonString)
customer := &Customer{}
err := json.Unmarshal(jsonByte, customer)
if err != nil {
panic(err)
}
fmt.Println(customer)
fmt.Println(customer.FirstName)
fmt.Println(customer.Address)
}

JSON Tag
Secara default attribute yang teradpat di Struct dan JSON akan di mapping sesuai dengan nama attribute yang sama (case sensitive). Kadang ada style yang berbeda antara penamaan attribute di Struct dan di JSON, misal di JSON kita ingin menggunakan snake_case, tapi di Struct, kita ingin menggunakan PascalCase.
Untungnya, package JSON mendukung Tag Reflection, kita bisa menambahkan tag rflection dengan nama json, lalu diikuti dengan attribute yang kita inginkan ketika konversi dari atau ke JSON.
Buatlah file baru dengan nama json_tag_test.go
dan masukkan baris kode berikut
package belajar_golang
import (
"encoding/json"
"fmt"
"testing"
)
type Product struct {
Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
ImageURL string `json:"image_url,omitempty"`
}
func TestJSONTagEncode(t *testing.T) {
product := Product{
Id: "P0001",
Name: "Apple Mac Book Pro",
ImageURL: "http://example.com/image.png",
}
bytes, err := json.Marshal(product)
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}
func TestJSONTagDecode(t *testing.T) {
jsonString := `{"id":"P0001","name":"Apple Mac Book Pro","image_url":"http://example.com/image.png"}`
jsonBytes := []byte(jsonString)
product := &Product{}
err := json.Unmarshal(jsonBytes, product)
if err != nil {
panic(err)
}
fmt.Println(product)
fmt.Println(product.Id)
fmt.Println(product.Name)
fmt.Println(product.ImageURL)
}

Map
Saat menggunakan JSON, kadang mungkin kita menemukan kasus data JSONnya dynamic, artinya attribute nya tidak menentu, bisa bertambah, bisa berkurang, dan tidak tetap. Pada kasus seperti itu, menggunakan Struct akan menyulitkan, karena pada Struct kita harus menentukan semua attributenya.
Untuk kasus seperti ini, kita bisa menggunakan tipe data map[string]interface{}
, secara otomatis attribute akan menjadi key di map, dan value menjadi value di map, namun karena value berupa interface{}
, maka kita harus lakukan konversi secara manual jika ingin mengambil value nya. Dan tipe data Map tidak mendukung JSON tag lagi.
package belajar_golang
import (
"encoding/json"
"fmt"
"testing"
)
func TestMapDecode(t *testing.T) {
jsonString := `{"id":"P0001","name":"Apple Mac Book Pro","image_url":"http://example.com/image.png"}`
jsonBytes := []byte(jsonString)
var result map[string]interface{}
json.Unmarshal(jsonBytes, &result)
fmt.Println(result)
fmt.Println(result["name"])
fmt.Println(result["image_url"])
}
func TestMapEncode(t *testing.T) {
product := map[string]interface{}{
"id": "P0001",
"name": "Apple Mac Book Pro",
"image_url": "http://example.com/image.png",
}
bytes, _ := json.Marshal(product)
fmt.Println(string(bytes))
}

Streaming Decoder
Sebelumnya kita belajar package json dengan melakukan konversi data JSON yang sudah dalam bentuk variable dan data string
atau []byte
. Pada kenyataannya, kadang data JSON nya berasal dari input berupa io.Reader(File, Network, Request Body)
.
Kita bisa saja membaca semua datanya terlebih dahulu, lalu simpan di variable, baru lakukan konversi dari JSON, namun hal ini sebenarnya tidak perlu dilakukan, karena package json memiliki fitur untuk membaca dari Stream.
Untuk membuat json Decoder, kita bisa gunakan function json.NewDecoder(reader)
, selanjutnya untuk membaca isi input reader dan konversikan secara langsung ke data di golang, cukup gunakan function Decode(interface{})
.
Buatlah file baru dengan nama Customer.json
dan masukkan baris kode berikut
{
"FirstName": "Rendy",
"Hobbies": null,
"Address": [
{
"Street": "Jalan Belum Ada",
"Country": "Indonesia",
"PostalCode": "9999"
},
{
"Street": "Jalan Lagi DIbangun",
"Country": "Indonesia",
"PostalCode": "8888"
}
]
}
Setelah data sample nya dibuat, selanjutnya buat kembali file baru dengan nama decoder_test.go
dan masukkan baris kode berikut
package belajar_golang
import (
"encoding/json"
"fmt"
"os"
"testing"
)
func TestStreamDecoder(t *testing.T) {
reader, err := os.Open("Customer.json")
if err != nil {
panic(err)
}
decoder := json.NewDecoder(reader)
customer := &Customer{}
decoder.Decode(customer)
fmt.Println(customer)
}

Streaming Encoder
Selain decoder, package json juga mendukung membuat Encoderyang bisa digunakan untuk menulis langsung JSON nya ke io.Writer
. Dengan begitu kita tidak perlu menyimpan JSON datanya terlebih dahulu ke dalam variable string
atau []byte
, kita bisa langsung tulis ke io.Writer
.
Untuk membuat encoder, kita bisa menggunakan function json.NewEncoder(writer)
, dan untuk menulis data sebagai JSON langsung ke writer, kita bisa gunakan function Encode(interface{})
.
Untuk mencobanya, buatlah file baru dengan nama encoder_test.go
dan masukkan baris kode berikut
package belajar_golang
import (
"encoding/json"
"os"
"testing"
)
func TestEncoder(t *testing.T) {
writer, err := os.Create("CustomerOut.json")
if err != nil {
panic(err)
}
encoder := json.NewEncoder(writer)
customer := Customer{
FirstName: "Rendy",
LastName: "Wijaya",
Age: 22,
}
err = encoder.Encode(customer)
if err != nil {
panic(err)
}
}

Penutup
Pada artikel kali ini kita telah belajar tentang json pada golang. Dan pada artikel selanjutnya saya akan membahas rest api pada golang.