Fundamentos de serialización
10 ejemplos para empezar con la serialización: 7 básicos y 3 intermedios.
Busca en todas las páginas de la documentación
10 ejemplos para empezar con la serialización: 7 básicos y 3 intermedios.
mkdir serdemo && cd serdemo && go mod init example.com/serdemo.main.go (o archivos separados en un mismo paquete) y ejecútalo con go run ..Realiza un viaje de ida y vuelta de un struct a través de bytes JSON.
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func main() {
u := User{ID: 1, Name: "Ada"}
b, err := json.Marshal(u)
if err != nil {
panic(err)
}
fmt.Println(string(b))
var decoded User
if err := json.Unmarshal(b, &decoded); err != nil {
panic(err)
}
fmt.Println(decoded.Name)
}Marshal devuelve []byte; conviértelo con string(b) para imprimir.Unmarshal necesita un puntero al valor de destino.Relacionado: Serialización en Go: JSON primero, formatos bajo demanda - por qué JSON es el predeterminado
Las etiquetas renombran campos y ocultan internos.
package main
import (
"encoding/json"
"fmt"
)
type Account struct {
Login string `json:"login"`
PasswordHash string `json:"-"`
Role string `json:"role,omitempty"`
}
func main() {
a := Account{Login: "ada", PasswordHash: "secret"}
b, _ := json.Marshal(a)
fmt.Println(string(b))
}json:"login" mapea Login a la clave "login".json:"-" excluye PasswordHash de la salida por completo.omitempty omite campos con valor cero como Role vacío.Relacionado: Etiquetas de Struct para JSON, DB y Validación - convenciones de múltiples etiquetas
Los punteros distinguen "ausente" de "presente cero".
package main
import (
"encoding/json"
"fmt"
)
type Item struct {
Qty int `json:"qty"`
Notes *string `json:"notes,omitempty"`
}
func main() {
empty := Item{Qty: 0}
b1, _ := json.Marshal(empty)
fmt.Println(string(b1))
note := ""
withPtr := Item{Qty: 1, Notes: ¬e}
b2, _ := json.Marshal(withPtr)
fmt.Println(string(b2))
}Qty con valor 0 todavía aparece a menos que agregues omitempty.Notes como nil se omite con omitempty; un puntero a "" se mantiene.null explícita.Relacionado: Evolución de esquemas y manejo de campos desconocidos - campos opcionales
Las colecciones se codifican como arrays y objetos JSON.
package main
import (
"encoding/json"
"fmt"
)
func main() {
tags := []string{"go", "json"}
meta := map[string]int{"a": 1, "b": 2}
b1, _ := json.Marshal(tags)
b2, _ := json.Marshal(meta)
fmt.Println(string(b1))
fmt.Println(string(b2))
}nil se codifican como null; los slices vacíos se codifican como [] cuando no son nil.encoding.TextMarshaler).map[string]any para contratos de API estables.Relacionado: Serialización personalizada de encoding/json - alternativas tipadas a los mapas
Transmite JSON a io.Writer y desde io.Reader.
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type Event struct {
Type string `json:"type"`
}
func main() {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
_ = enc.Encode(Event{Type: "click"})
dec := json.NewDecoder(&buf)
var e Event
_ = dec.Decode(&e)
fmt.Println(e.Type)
}Encode agrega una nueva línea después de cada valor (compatible con JSON Lines).Relacionado: Mejores prácticas de serialización - límites de tamaño en los cuerpos
La incrustación anónima aplana la salida JSON.
package main
import (
"encoding/json"
"fmt"
)
type Timestamps struct {
CreatedAt string `json:"created_at"`
}
type Post struct {
Timestamps
Title string `json:"title"`
}
func main() {
p := Post{Timestamps: Timestamps{CreatedAt: "2026-01-01"}, Title: "Hi"}
b, _ := json.Marshal(p)
fmt.Println(string(b))
}Relacionado: Serialización personalizada de encoding/json - incrustación con serializadores personalizados
MarshalIndent formatea la salida durante el desarrollo.
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := map[string]any{"ok": true, "count": 3}
b, _ := json.MarshalIndent(data, "", " ")
fmt.Println(string(b))
}Relacionado: JSON de alto rendimiento: alternativas jsoniter y sonic - cuando la velocidad compacta importa
Anula la codificación de fecha predeterminada RFC3339.
package main
import (
"encoding/json"
"fmt"
"time"
)
type DateOnly time.Time
func (d DateOnly) MarshalJSON() ([]byte, error) {
t := time.Time(d)
return json.Marshal(t.Format("2006-01-02"))
}
func main() {
d := DateOnly(time.Date(2026, 7, 15, 0, 0, 0, 0, time.UTC))
b, _ := json.Marshal(d)
fmt.Println(string(b))
}time.Time ya implementa MarshalJSON con RFC3339; los nuevos tipos lo envuelven para otros formatos.MarshalJSON con UnmarshalJSON para API simétricas.Relacionado: Serialización personalizada de encoding/json - patrones de serializador personalizado completos
Mantén JSON desconocido o parcial hasta que conozcas el esquema.
package main
import (
"encoding/json"
"fmt"
)
type Envelope struct {
Type string `json:"type"`
Payload json.RawMessage `json:"payload"`
}
func main() {
raw := []byte(`{"type":"user","payload":{"id":1}}`)
var env Envelope
_ = json.Unmarshal(raw, &env)
fmt.Println(env.Type, string(env.Payload))
}RawMessage es un alias de []byte que retrasa la decodificación anidada.Type.Relacionado: Evolución de esquemas y manejo de campos desconocidos - patrones de evolución
Rechaza solicitudes con claves inesperadas.
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type CreateUser struct {
Name string `json:"name"`
}
func main() {
body := []byte(`{"name":"Ada","admin":true}`)
dec := json.NewDecoder(bytes.NewReader(body))
dec.DisallowUnknownFields()
var req CreateUser
err := dec.Decode(&req)
fmt.Println(err)
}Relacionado: Validación con go-playground/validator - reglas post-decodificación
Versiones de la pila: Esta página se escribió para Go 1.26.x (GC predeterminado Green Tea, modernizadores go fix - verifica el parche en la compilación), chi (última versión - verifica en la compilación), gin (última versión - verifica en la compilación), echo (última versión - verifica en la compilación), google.golang.org/grpc (última versión - verifica en la compilación), sigs.k8s.io/controller-runtime (última versión - verifica en la compilación), kubebuilder (última versión - verifica en la compilación), tinygo (última versión - verifica los objetivos de placa en la compilación), wazero (última versión - verifica en la compilación) y golangci-lint (última versión - verifica el conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 18 jul 2026