encoding/json with Structs
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 76 of 107.
The encoding/json package lets you convert Go structs to JSON and back. This is essential for APIs, configuration files, and data storage. Struct tags control how fields map to JSON keys.
To convert a struct to JSON, use json.Marshal:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func main() {
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}
}The struct tags like `json:"name"` specify the JSON key names. Without tags, Go uses the field name directly. Only exported fields (capitalized) are included in the JSON output.
To convert JSON back to a struct, use json.Unmarshal:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobCommon tag options include omitempty to skip zero values and - to ignore a field entirely:
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}With omitempty, if Price is zero, it won't appear in the JSON. The Internal field is never included regardless of its value.
Challenge
EasyLet's build a movie catalog system that converts between Go structs and JSON! You'll practice marshaling structs to JSON and unmarshaling JSON back to structs, using struct tags to control the JSON field names.
You'll organize your code across two files:
movie.go: Define your movie data model with proper JSON tags.Create a
Moviestruct with the following fields and JSON mappings:Title(string) maps to"title"Director(string) maps to"director"Year(int) maps to"year"Rating(float64) maps to"rating"withomitemptyso unrated movies don't show a rating fieldInternalID(string) should be excluded from JSON entirely using"-"
Create a function
ToJSON(m Movie) stringthat converts a Movie to a JSON string. Also createFromJSON(jsonStr string) Moviethat parses a JSON string back into a Movie struct.main.go: Demonstrate JSON conversion in both directions.Read an operation type (
encodeordecode).For
encode: Read movie details (title, director, year, rating) on separate lines. Create a Movie with an InternalID of"internal-123", convert it to JSON, and print the result.For
decode: Read a JSON string, convert it to a Movie struct, and print the movie details in this format:Title: [title] Director: [director] Year: [year] Rating: [rating]Display the rating with one decimal place. If the rating is 0 (not present in JSON), print
Rating: N/Ainstead.
The following inputs will be provided:
- Line 1: Operation type (
encodeordecode) - For encode: title, director, year, rating (four lines)
- For decode: a JSON string (one line)
For example, given:
encode
Inception
Christopher Nolan
2010
8.8Your output should be:
{"title":"Inception","director":"Christopher Nolan","year":2010,"rating":8.8}Notice that InternalID doesn't appear in the output because of the "-" tag.
And given:
encode
The Matrix
Wachowskis
1999
0Your output should be:
{"title":"The Matrix","director":"Wachowskis","year":1999}Notice that rating is omitted because it's zero and has omitempty.
And given:
decode
{"title":"Interstellar","director":"Christopher Nolan","year":2014,"rating":8.6}Your output should be:
Title: Interstellar
Director: Christopher Nolan
Year: 2014
Rating: 8.6And given:
decode
{"title":"Tenet","director":"Christopher Nolan","year":2020}Your output should be:
Title: Tenet
Director: Christopher Nolan
Year: 2020
Rating: N/ACheat sheet
The encoding/json package converts Go structs to JSON and vice versa using json.Marshal and json.Unmarshal.
To convert a struct to JSON:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}To convert JSON back to a struct:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobStruct tags control JSON field mapping:
`json:"name"`- maps field to JSON key "name"`json:"price,omitempty"`- omits field if zero value`json:"-"`- excludes field from JSON entirely
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}Only exported fields (capitalized) are included in JSON output.
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Read operation type
operation, _ := reader.ReadString('\n')
operation = strings.TrimSpace(operation)
if operation == "encode" {
// Read movie details
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
director, _ := reader.ReadString('\n')
director = strings.TrimSpace(director)
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
ratingStr, _ := reader.ReadString('\n')
ratingStr = strings.TrimSpace(ratingStr)
rating, _ := strconv.ParseFloat(ratingStr, 64)
// TODO: Create a Movie struct with the read values
// Set InternalID to "internal-123"
// Convert to JSON using ToJSON and print the result
} else if operation == "decode" {
// Read JSON string
jsonStr, _ := reader.ReadString('\n')
jsonStr = strings.TrimSpace(jsonStr)
// TODO: Convert JSON to Movie using FromJSON
// Print the movie details in the required format
// Use fmt.Sprintf("%.1f", rating) for rating formatting
// If rating is 0, print "Rating: N/A" instead
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser11Standard Library & OOP
io.Reader & io.Writersort.Interfacefmt.Stringer Interfaceencoding/json with Structshttp.Handler InterfaceRecap - REST API Models3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool