Skip to main content

Go Ep 6: Structs, Fields & JSON Serialization

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
go - This article is part of a series.
Part 6: This Article
Go is not a class-based object-oriented language. Instead, Go uses Structs to group typed fields together into contiguous memory blocks.

TL;DR (Quick Summary)
#

  • Struct: A typed collection of fields. Example: type User struct { Name string; Age int }.
  • Field Visibility: Capitalized fields (Name) are Exported (public across packages). Lowercase fields (password) are Unexported (private to current package).
  • Constructor Function: Idiomatic Go uses factory functions like NewUser(name string) *User to instantiate structs safely.
  • Struct Tags: Metadata annotations (json:"user_id,omitempty") controlling JSON encoding and decoding via reflection.

1. Declaring Structs & Factory Constructors
#

In Go, there are no class keywords or automatic class constructors. The idiomatic pattern is to write a constructor function prefixed with New.

package main

import "fmt"

// User struct definition
type User struct {
	ID       string // Exported (Public)
	Username string // Exported (Public)
	email    string // Unexported (Private to this package)
}

// Idiomatic Factory Constructor Function
func NewUser(id, username, email string) *User {
	return &User{
		ID:       id,
		Username: username,
		email:    email,
	}
}

// Getter method for unexported email field
func (u *User) GetEmail() string {
	return u.email
}

func main() {
	// Instantiate via Constructor
	u := NewUser("usr_100", "rachmat", "rachmat@work.com")

	fmt.Printf("User ID: %s, Name: %s\n", u.ID, u.Username)
	fmt.Printf("Private Email: %s\n", u.GetEmail())
}

2. Anonymous Structs & Struct Literals
#

For one-off data structures (like test cases or single HTTP response payloads), you can declare Anonymous Structs without defining a type name.

package main

import "fmt"

func main() {
	// Anonymous struct declaration and inline initialization
	config := struct {
		Environment string
		Port        int
	}{
		Environment: "production",
		Port:        8080,
	}

	fmt.Printf("Config: %s on port %d\n", config.Environment, config.Port)
}

3. Serialization with Struct Tags (json:"...")
#

Struct Tags provide runtime metadata annotations processed by reflection (such as encoding/json or gorm).

package main

import (
	"encoding/json"
	"fmt"
)

type Product struct {
	ID          int     `json:"product_id"`
	Name        string  `json:"name"`
	Price       float64 `json:"price"`
	Discount    float64 `json:"discount,omitempty"` // Omit key if 0.0
	internalSKU string  `json:"-"`                  // Completely ignore in JSON
}

func main() {
	p1 := Product{
		ID:          501,
		Name:        "Mechanical Keyboard",
		Price:       150.00,
		Discount:    0.0, // Zero value -> will be omitted in JSON!
		internalSKU: "SKU-999-SECRET",
	}

	// Marshal struct to formatted JSON bytes
	jsonData, _ := json.MarshalIndent(p1, "", "  ")
	fmt.Println(string(jsonData))
}

Expected Terminal Output:

{
  "product_id": 501,
  "name": "Mechanical Keyboard",
  "price": 150
}

Notice discount was omitted because of omitempty, and internalSKU was ignored because it is an unexported private field!


4. Struct Memory Alignment & Padding (Advanced)
#

Field ordering inside a struct impacts its overall memory footprint on 64-bit CPU architectures due to Memory Alignment (Padding).

// 🔴 Bad Field Alignment: Size = 24 bytes (Due to 7 bytes padding after bool!)
type BadStruct struct {
	FlagA bool  // 1 byte
	Value int64 // 8 bytes
	FlagB bool  // 1 byte
}

// 🟢 Optimized Field Alignment: Size = 16 bytes (Fields grouped by word size!)
type GoodStruct struct {
	Value int64 // 8 bytes
	FlagA bool  // 1 byte
	FlagB bool  // 1 byte
}

Summary & Next Steps
#

In this episode:

  • We declared Go Structs and written idiomatic NewUser() constructor functions.
  • We controlled field visibility (Exported vs Unexported).
  • We serialized structs using JSON field tags (json:"name,omitempty").
  • We analyzed struct memory padding optimization.

Check the rest of the Go Master Series in content/series/go/!

go - This article is part of a series.
Part 6: This Article