JSON y serialización de datos

Intermedio
Go
Go
Actualizado: 03/04/2026

Serialización básica: json.Marshal

json.Marshal convierte un valor Go a JSON:

JSON y serialización en Go: Marshal, Unmarshal, Encoder/Decoder y struct tags

type Producto struct {
    ID     int     `json:"id"`
    Nombre string  `json:"nombre"`
    Precio float64 `json:"precio"`
}

p := Producto{ID: 1, Nombre: "Teclado", Precio: 59.99}

datos, err := json.Marshal(p)
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(datos))
// {"id":1,"nombre":"Teclado","precio":59.99}

// Formato legible (pretty print)
datosFormateados, _ := json.MarshalIndent(p, "", "  ")
fmt.Println(string(datosFormateados))

Etiquetas de struct

Las etiquetas json: controlan cómo se serializa cada campo:

type Usuario struct {
    ID           int        `json:"id"`
    Nombre       string     `json:"nombre"`
    Email        string     `json:"email"`
    Contraseña   string     `json:"-"`              // nunca serializar
    Biografia    string     `json:"biografia,omitempty"` // omitir si vacío
    Edad         int        `json:"edad,omitempty"`      // omitir si 0
    FechaCreacion time.Time `json:"fecha_creacion"`
}

| Etiqueta | Efecto | |----------|--------| | json:"nombre" | Usa "nombre" como clave JSON | | json:"-" | Nunca incluir en JSON | | json:",omitempty" | Omitir si el valor es el zero value | | json:",string" | Codificar el número como cadena |

Deserialización: json.Unmarshal

jsonData := []byte(`{"id":2,"nombre":"Ratón","precio":25.50}`)
var p Producto
if err := json.Unmarshal(jsonData, &p); err != nil {
    log.Fatalf("Error al deserializar: %v", err)
}
fmt.Printf("ID: %d, Nombre: %s, Precio: %.2f\n", p.ID, p.Nombre, p.Precio)

Los campos desconocidos se ignoran por defecto, y los campos ausentes mantienen el zero value del tipo.

Streams: json.Encoder y json.Decoder

Para procesar JSON desde/hacia streams (ficheros, HTTP, etc.) sin cargar todo en memoria:

// Codificar al stream
func servirProductos(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    productos := obtenerProductos()
    if err := json.NewEncoder(w).Encode(productos); err != nil {
        log.Println("Error al codificar:", err)
    }
}

// Decodificar desde el stream
func recibirProducto(r *http.Request) (*Producto, error) {
    var p Producto
    decoder := json.NewDecoder(r.Body)
    decoder.DisallowUnknownFields() // error si hay campos inesperados
    if err := decoder.Decode(&p); err != nil {
        return nil, fmt.Errorf("JSON inválido: %w", err)
    }
    return &p, nil
}

Campos opcionales con punteros

Usar punteros permite distinguir entre "campo ausente" y "campo con valor cero":

type Configuracion struct {
    Host    string  `json:"host"`
    Puerto  *int    `json:"puerto,omitempty"` // nil si no viene en el JSON
    TLS     *bool   `json:"tls,omitempty"`
}

// Verificar si el campo vino en el JSON
conf := &Configuracion{}
json.Unmarshal(datos, conf)

if conf.Puerto == nil {
    fmt.Println("Puerto no especificado, usando 8080")
} else {
    fmt.Println("Puerto:", *conf.Puerto)
}

Fechas y tiempo

time.Time se serializa automáticamente en formato RFC 3339:

type Evento struct {
    Nombre  string    `json:"nombre"`
    Fecha   time.Time `json:"fecha"`
}

e := Evento{Nombre: "Lanzamiento", Fecha: time.Now()}
datos, _ := json.Marshal(e)
// {"nombre":"Lanzamiento","fecha":"2026-03-30T10:00:00Z"}

JSON dinámico con map y interface{}

Para JSON con estructura variable:

// Deserializar JSON de estructura desconocida
var resultado map[string]any
json.Unmarshal(datos, &resultado)

// Acceder a campos
if nombre, ok := resultado["nombre"].(string); ok {
    fmt.Println("Nombre:", nombre)
}

// JSON anidado
var anidado map[string]map[string]any
json.Unmarshal(datosAnidados, &anidado)

Marshaling personalizado

Implementa json.Marshaler y json.Unmarshaler para control total:

type Temperatura struct {
    Celsius float64
}

func (t Temperatura) MarshalJSON() ([]byte, error) {
    return json.Marshal(map[string]any{
        "celsius":    t.Celsius,
        "fahrenheit": t.Celsius*9/5 + 32,
    })
}

func (t *Temperatura) UnmarshalJSON(datos []byte) error {
    var v struct {
        Celsius float64 `json:"celsius"`
    }
    if err := json.Unmarshal(datos, &v); err != nil {
        return err
    }
    t.Celsius = v.Celsius
    return nil
}

Validación de JSON entrante

func procesarPeticion(w http.ResponseWriter, r *http.Request) {
    if r.Header.Get("Content-Type") != "application/json" {
        http.Error(w, "Content-Type debe ser application/json",
            http.StatusUnsupportedMediaType)
        return
    }

    r.Body = http.MaxBytesReader(w, r.Body, 1_048_576) // límite de 1 MB

    decoder := json.NewDecoder(r.Body)
    decoder.DisallowUnknownFields()

    var p Producto
    if err := decoder.Decode(&p); err != nil {
        var syntaxErr *json.SyntaxError
        if errors.As(err, &syntaxErr) {
            http.Error(w, "JSON malformado", http.StatusBadRequest)
            return
        }
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Procesar p...
}
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Go es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Go

Explora más contenido relacionado con Go y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Serializar structs a JSON con json.Marshal y json.MarshalIndent.
  • Deserializar JSON en structs con json.Unmarshal.
  • Usar etiquetas json: para controlar nombres de campos, omitempty y -.
  • Codificar y decodificar JSON en streams con json.Encoder y json.Decoder.
  • Manejar tipos especiales como fechas, números y campos opcionales con punteros.
  • Implementar json.Marshaler y json.Unmarshaler para serialización personalizada.