encoding/json convierte un struct en JSON con una llamada. Lo que decide el resultado son las etiquetas de cada campo y una regla de Go que sorprende la primera vez: los campos en minúscula no salen.
También te puede interesar
Sin etiquetas
type SinEtiquetas struct {
Nombre string
correo string
}
b, _ := json.Marshal(SinEtiquetas{"Ana", "ana@ejemplo.com"})
fmt.Println(string(b))salida{"Nombre":"Ana"}Dos cosas. La clave sale con mayúscula, como el campo. Y el correo ha desaparecido: en Go, un nombre que empieza por minúscula es privado del paquete, y encoding/json, que está en otro paquete, no puede verlo. No da error; simplemente lo ignora.
Con etiquetas
type Usuario struct {
Nombre string `json:"nombre"`
Correo string `json:"correo"`
Password string `json:"-"`
Edad int `json:"edad,omitempty"`
Alta time.Time `json:"alta,omitzero"`
}con Password, sin Edad ni Alta{"nombre":"Ana","correo":"ana@ejemplo.com"}| Etiqueta | Qué hace |
|---|---|
json:"nombre" | el nombre de la clave en el JSON |
json:"-" | no sale nunca |
omitempty | no sale si vale cero, cadena vacía, nil o colección vacía |
omitzero | no sale si es el valor cero de su tipo; desde Go 1.24 |
json:"password" y aparecía en el resultado. Basta con que algún día alguien devuelva ese struct en una respuesta de una API para que las contraseñas viajen a quien las pida. json:"-" lo impide de raíz.¿Por qué existe omitzero si ya había omitempty? Porque una fecha vacía, un time.Time, es un struct, y para omitempty un struct nunca está «vacío»: la fecha salía como "0001-01-01T00:00:00Z". omitzero lo resuelve.
Con sangría
b, _ := json.MarshalIndent(u, "", " ")
salida{
"nombre": "Ana",
"correo": "ana@ejemplo.com",
"edad": 30,
"alta": "2026-10-08T09:30:00Z"
}De JSON a struct
var leido Usuario
err := json.Unmarshal([]byte(`{"nombre":"Luis","edad":"treinta"}`), &leido)
fmt.Println("error:", err)salidaerror: json: cannot unmarshal string into Go struct field Usuario.edad of type int
Hay que pasar un puntero, &leido, para que pueda rellenarlo. Si un tipo no encaja, el error dice exactamente qué campo y qué esperaba. Lo contrario, en cambio, no da error: las claves que no existen en el struct se ignoran sin avisar, así que una errata en el nombre de un campo hace que llegue vacío y no te enteras. Si quieres que falle, usa un json.Decoder con DisallowUnknownFields().
Sin struct
var cualquiera map[string]any
json.Unmarshal([]byte(`{"n": 42, "lista": [1, 2]}`), &cualquiera)
fmt.Printf("%T %v | %T\n", cualquiera["n"], cualquiera["n"], cualquiera["lista"])salidafloat64 42 | []interface {}Cuando no sabes la forma del JSON. Cuidado: todos los números llegan como float64, también los enteros, y para usarlos hay que convertirlos.
Más información en inglés: Convert a struct to JSON in Golang.
Todo el código se compiló y se ejecutó con Go 1.27 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.