Question
I am trying to convert a Go struct to JSON using Go's encoding/json package, but the result is always an empty object: {}.
Here is the code:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
name string
}
func main() {
user := &User{name: "Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Printf("Error: %s", err)
return
}
fmt.Println(string(b))
}
When I run it, the output is:
{}
Why does json.Marshal produce {} instead of including the name field, and how do I fix it?
Short Answer
By the end of this page, you will understand why json.Marshal ignores some struct fields in Go, how exported vs unexported fields work, and how to correctly produce JSON using exported field names and optional struct tags.
Concept
In Go, the encoding/json package only reads exported struct fields when converting a struct to JSON.
A field is exported if its name starts with an uppercase letter:
type User struct {
Name string
}
A field is unexported if it starts with a lowercase letter:
type User struct {
name string
}
In your original code, the field is name, which is unexported. Because of that, json.Marshal cannot include it in the JSON output, so the result is:
{}
This rule exists because Go uses capitalization to control visibility across packages. The encoding/json package is a separate package, so it can only access exported fields.
To fix the problem, rename the field to Name:
type User struct {
Name string
}
Now json.Marshal can access it and will output:
Mental Model
Think of a Go struct as a building with many rooms.
- Exported fields are rooms with open doors and labels on them, so other packages can enter and read them.
- Unexported fields are rooms with locked doors, visible only inside the same package.
The encoding/json package is like a visitor from another building. It can only enter rooms with open doors.
So if your struct looks like this:
type User struct {
name string
}
json.Marshal walks up to the name field, finds the door locked, and skips it. With no readable fields left, it outputs:
{}
If you change it to:
type User struct {
Name string
}
now the door is open, and json.Marshal can include it in the JSON.
Syntax and Examples
Basic fix
Make the field exported by starting it with an uppercase letter.
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string
}
func main() {
user := &User{Name: "Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(string(b))
}
Output:
{"Name":"Frank"}
Using a JSON tag for lowercase keys
If you want the JSON field name to be name instead of Name, add a tag:
type User struct {
Name string `json:"name"`
}
Example:
Step by Step Execution
Consider this working example:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
}
func main() {
user := User{Name: "Frank"}
b, err := json.Marshal(user)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(b))
}
Step-by-step trace
1. Define the struct
type User struct {
Name string `json:"name"`
}
- The struct has one field:
Name - It starts with uppercase
N, so it is exported - The JSON tag says: use
nameas the key in JSON
2. Create a value
user := User{Name: "Frank"}
Now the value contains:
Real World Use Cases
This concept appears constantly in Go development.
Building JSON APIs
When returning data from an HTTP handler, structs must use exported fields:
type Response struct {
Message string `json:"message"`
Success bool `json:"success"`
}
If fields are lowercase, clients may receive empty objects or missing data.
Parsing and generating request/response bodies
Go services often send and receive JSON between microservices. Exported fields ensure the payload contains the expected values.
Configuration files
Applications may load or save JSON config files using structs. If fields are unexported, config values may not be written correctly.
Database and API models
Developers often define structs that are reused for:
- database records
- API responses
- background jobs
- event payloads
Correct field exporting and JSON tags keep data consistent across systems.
Logging structured data
Some systems serialize structs into JSON logs. Exported fields determine what appears in the log output.
Real Codebase Usage
In real projects, developers usually combine exported fields with JSON tags.
Common pattern: exported field + lowercase JSON key
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
This keeps Go code idiomatic while matching common JSON naming conventions.
Validation before marshaling
Developers often validate struct values before returning them:
func buildUserResponse(name string) ([]byte, error) {
if name == "" {
return nil, fmt.Errorf("name cannot be empty")
}
user := User{Name: name}
return json.Marshal(user)
}
This uses a guard clause to fail early.
Hiding internal fields
Real codebases often keep internal data out of JSON intentionally:
type User struct {
ID int
Name
password
}
Common Mistakes
1. Using lowercase field names
Broken code:
type User struct {
name string
}
Problem:
nameis unexportedjson.Marshalignores it
Fix:
type User struct {
Name string
}
2. Thinking JSON tags make unexported fields work
Broken code:
type User struct {
name string `json:"name"`
}
Problem:
- The tag does not make the field exported
- The field is still ignored
Fix:
type User struct {
Name string `json:"name"`
}
3. Forgetting to convert bytes to string when printing
Code:
Comparisons
Exported vs unexported fields in Go
| Field name | Exported? | Accessible from other packages? | Included by json.Marshal? |
|---|---|---|---|
Name | Yes | Yes | Yes |
name | No | No | No |
Without tag vs with tag
| Struct field | JSON output |
|---|---|
Name string | {"Name":"Frank"} |
Name string \json:"name"`` |
Cheat Sheet
Quick rule
json.Marshal only includes exported struct fields.
Exported field
type User struct {
Name string
}
Unexported field
type User struct {
name string
}
This becomes:
{}
Lowercase JSON key with tag
type User struct {
Name string `json:"name"`
}
Output:
{"name":"Frank"}
Core syntax
b, err := json.Marshal(value)
if err != nil {
}
fmt.Println((b))
FAQ
Why does Go json.Marshal return {} for my struct?
Because your struct fields are unexported, usually due to starting with lowercase letters. encoding/json only marshals exported fields.
Do I need to use uppercase field names in Go structs for JSON?
Yes, if you want encoding/json to access those fields. Use uppercase Go field names and JSON tags if you want lowercase JSON keys.
Can I use a JSON tag on a lowercase field in Go?
You can write the tag, but it will not help. Unexported fields are still ignored.
How do I make the JSON output use name instead of Name?
Use a tag:
Name string `json:"name"`
Does json.Marshal work differently for pointers and struct values?
No. Both pointers and values work. The issue is field visibility, not pointer usage.
Why doesn't Go return an error when fields are skipped?
Skipping unexported fields is expected behavior, so encoding/json simply ignores them instead of treating them as an error.
How can I hide a field from JSON on purpose?
Use an unexported field or use a tag like:
Mini Project
Description
Create a small Go program that converts user data into JSON for an API-style response. This demonstrates the exact rule behind the original problem: exported struct fields are included in JSON, and JSON tags control the final key names.
Goal
Build a program that marshals a User struct into readable JSON with lowercase JSON field names.
Requirements
- Define a
Userstruct with at least three exported fields. - Use JSON struct tags so the output keys are lowercase.
- Create a user value with sample data.
- Convert the struct to JSON using
json.Marshal. - Print the JSON output as a string.
Keep learning
Related questions
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Check if a Value Exists in a Slice in Go
Learn how to check whether a value exists in a slice in Go, and why Go has no Python-style `in` operator for arrays or slices.
Concatenating Slices in Go with append
Learn how to concatenate two slices in Go using append and the ... operator, with examples, pitfalls, and practical usage.