Go Struct Embedding for JSON Composition
Learn how Go struct embedding flattens JSON fields from multiple types into a single struct. Covers composition patterns and field promotion.
Detailed Explanation
Struct Embedding for JSON Composition
Go struct embedding (anonymous fields) lets you compose types by including one struct inside another without a field name. The embedded struct's fields are "promoted" to the parent level.
The JSON Pattern
{
"id": 1,
"name": "Alice",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-20T15:30:00Z"
}
Embedded Structs
type Timestamps struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Timestamps // embedded — fields are promoted
}
How It Works with JSON
When Go unmarshals JSON into User, it sees created_at and updated_at as if they were direct fields of User:
var user User
json.Unmarshal(data, &user)
fmt.Println(user.CreatedAt) // works — promoted field
And when marshaling, the embedded fields are serialized flat, not nested:
// Output: {"id":1,"name":"Alice","created_at":"...","updated_at":"..."}
Common Composition Patterns
type BaseModel struct {
ID int `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type User struct {
BaseModel
Name string `json:"name"`
Email string `json:"email"`
}
type Product struct {
BaseModel
Title string `json:"title"`
Price float64 `json:"price"`
}
Both User and Product inherit ID, CreatedAt, and UpdatedAt without duplication.
Field Shadowing
If the parent defines a field with the same JSON key as the embedded struct, the parent's field takes precedence. This lets you override specific fields when needed.
Embedding vs Named Fields
Embedding flattens the JSON. A named field nests it:
type A struct { Timestamps } // flat JSON
type B struct { TS Timestamps } // nested under "TS"
Choose embedding when the JSON is flat and the types share common fields. Choose named fields when the JSON is explicitly nested.
Use Case
ORMs like GORM use struct embedding for base model fields (ID, timestamps). When your API returns flat JSON that shares common fields across multiple resource types, embedding avoids code duplication.