Serialización en Go: JSON Primero, Formatos Bajo Demanda
Los servicios de Go pasan la mayor parte de su tiempo de serialización convirtiendo structs a JSON y viceversa.
Busca en todas las páginas de la documentación
Los servicios de Go pasan la mayor parte de su tiempo de serialización convirtiendo structs a JSON y viceversa.
La biblioteca estándar incluye codificadores diseñados específicamente para formatos comunes y un pequeño conjunto de interfaces te permite anular el comportamiento cuando los valores predeterminados son incorrectos.
Conceptos básicos de serialización recopila fragmentos ejecutables; las páginas hermanas cubren el marshaling personalizado, formatos alternativos, etiquetas de struct, evolución del esquema, bibliotecas de rendimiento, validación y convenciones de equipo.
encoding/json es la opción predeterminada; otros paquetes en encoding/* y códecs de terceros satisfacen necesidades especializadas.La serialización responde a una pregunta: ¿cómo conviertes un struct User en bytes que otro programa pueda leer?
La respuesta de Go comienza con codificadores impulsados por reflexión en la biblioteca estándar.
json.Marshal recorre los campos exportados del struct, lee las etiquetas json y emite JSON UTF-8.
json.Unmarshal hace lo contrario, asignando structs y slices anidados según sea necesario.
El patrón se repite en todos los formatos:
| Paquete | Uso típico | Forma de red |
|---|---|---|
encoding/json | APIs REST, archivos de configuración | JSON de texto |
encoding/xml | SOAP, RSS, empresas heredadas | XML de texto |
encoding/gob | RPC de Go a Go o cachés | Binario, específico de Go |
google.golang.org/protobuf | gRPC, contratos multilingües | Protobuf binario |
La mayoría de las rutas de código de producción de Go tocan JSON primero.
Los manejadores de chi, gin y echo enlazan cuerpos JSON a través de encoding/json o envoltorios delgados.
google.golang.org/grpc utiliza protobuf, no JSON, en la red (las pasarelas pueden transcodificar a JSON).
Las etiquetas de struct son la superficie de configuración principal:
type Profile struct {
ID string `json:"id"`
Email string `json:"email,omitempty"`
Internal string `json:"-"`
CreatedAt time.Time `json:"created_at"`
}omitempty omite los valores cero de la salida.- oculta un campo de JSON por completo.Cuando las etiquetas no son suficientes, implementa json.Marshaler y json.Unmarshaler:
func (t TimeOnly) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Format("15:04:05"))
}El codificador verifica estas interfaces antes de usar las reglas predeterminadas.
Las APIs de streaming (json.Encoder, json.Decoder) son adecuadas para cuerpos de respuesta HTTP y entradas grandes sin cargar todo en memoria.
Las opciones del decodificador son importantes en producción:
Decoder.DisallowUnknownFields() rechaza claves inesperadas (APIs estrictas).UseNumber() mantiene los enteros grandes como json.Number en lugar de float64.La validación pertenece a un paso separado.
go-playground/validator lee las etiquetas validate después de que JSON aterriza en un struct.
La serialización no impone reglas de negocio; solo traduce formas.
La evolución del esquema es un problema de contrato.
Los servidores deben agregar campos sin romper clientes antiguos; los clientes deben ignorar claves desconocidas.
Protobuf formaliza los números de campo; JSON se basa en la disciplina y las pruebas.
json.RawMessage pospone la decodificación de blobs anidados para que puedas decodificar cargas útiles parciales de forma compatible con versiones anteriores.
El rendimiento se vuelve relevante en QPS altos o trabajos por lotes grandes.
json-iterator y sonic (verifica las versiones en la compilación) reflejan las APIs de encoding/json con reflexión más rápida o rutas JIT.
Los riesgos de migración incluyen diferencias sutiles de comportamiento en omitempty, el manejo de float64 y el orden de MarshalJSON.
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
encoding/json | Estable, sin dependencias | Más lento en cargas útiles enormes | APIs HTTP públicas |
| Protobuf + gRPC | Esquema binario compacto y versionado | Requiere codegen y herramientas | Microservicios internos |
encoding/gob | Binario nativo de Go simple | No es multilingüe | Cachés locales del proceso |
encoding/xml | Interoperabilidad empresarial | Verboso, frágil | Fuentes heredadas |
MarshalJSON personalizado | Control total | Fácil de equivocarse | Formatos de fecha, enums, redacción |
Los límites de tamaño pertenecen a la capa HTTP (http.MaxBytesReader) y a la política de la aplicación, no dentro de json.Marshal.
golangci-lint puede marcar etiquetas de struct y etiquetas JSON que se desvían de los nombres de campo cuando los equipos habilitan linters de verificación de etiquetas.
"", 0, false, punteros nil, slices vacíos y times cero.json, db y validate juntos.JSON es legible por humanos, depurable con curl y compatible con todas las pilas de clientes.
El codificador de la biblioteca estándar es lo suficientemente bueno para la mayoría de los presupuestos de latencia de API.
Utiliza protobuf para servicios gRPC internos que necesiten cargas útiles binarias compactas y tipos multilingües generados.
Mantén JSON en el borde para navegadores y REST públicos.
Implementa ambos cuando el tipo recorra las APIs.
Las implementaciones unilaterales confunden a los lectores y rompen las pruebas de simetría.
gin usa ShouldBindJSON; echo usa Bind; chi se empareja con json.NewDecoder(r.Body).
Todos dependen en última instancia de encoding/json o decodificadores compatibles.
Usa gopkg.in/yaml.v3 o github.com/pelletier/go-toml/v2 para archivos de configuración.
Mantén las APIs HTTP en JSON a menos que los clientes requieran lo contrario.
No con los codificadores basados en reflexión de la biblioteca estándar.
Usa marshalers personalizados o exporta campos destinados a formatos de red.
Usa json:"-", MarshalJSON personalizado o un DTO de registro dedicado sin campos sensibles.
Nunca confíes en omitempty para contraseñas.
Mayormente sí, pero los límites de reflexión se aplican en placas pequeñas.
Verifica tu objetivo con la nota del pie de página de la pila en el momento de la compilación.
Elige una convención por superficie de API y codifícala en etiquetas de struct.
Documenta la elección en tu OpenAPI o README.
Envuelve r.Body con http.MaxBytesReader antes de decodificar.
Rechaza cargas útiles demasiado grandes antes de que Unmarshal asigne slices grandes.
Versiones de la pila: Esta página fue escrita para Go 1.26.x (predeterminado de GC Green Tea, go fix modernizers - 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 la 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: 16 jul 2026