Convertir JSON a Go
JSON a Go genera structs a partir de un documento de ejemplo, en tu navegador. Cada campo es exportado y lleva una etiqueta json explícita, porque encoding/json ignora en silencio los campos no exportados — un nombre de campo en minúsculas es la razón más común de que un struct generado se serialice como un objeto vacío.
El documento termina antes de tiempo; parece incompleto.
^
¿Cómo funciona Convertir JSON a Go?
Tres decisiones hacen que un struct de Go generado sea correcto o esté roto en silencio, y las tres tratan de lo que encoding/json hace en tiempo de ejecución, no del aspecto del código.
Cada campo está exportado y etiquetado
Go solo serializa los campos exportados. Un struct con un campo llamado id produce {} a partir de json.Marshal y deja id en cero tras json.Unmarshal, sin error en ningún caso. Por eso cada nombre va en mayúsculas y recibe una etiqueta json:"…" explícita con la clave original. Las siglas siguen la propia guía de estilo de Go: user_id se convierte en UserID, no en UserId, y api_url se convierte en APIURL.
Los campos opcionales son punteros
Un string normal no puede contener un null de JSON, y no puede distinguir una clave ausente de una vacía — ambas llegan como "". Un int normal no puede distinguir un count ausente de un count en cero. Por eso los campos opcionales y que admiten null se convierten en punteros, la única construcción en Go que conserva la distinción. Los slices, los maps y interface{} ya admiten nil, así que se dejan como están en vez de darles una segunda indirección sin sentido.
Los enteros que int no puede contener se convierten en int64
int es de 32 bits en algunas plataformas. Un ID snowflake o una marca de tiempo en milisegundos lo desborda, y el bug resultante aparece solo en la plataforma donde es estrecho. Los valores por encima de 2,147,483,647 se emiten como int64.
Las uniones son interface{}, porque Go no tiene tipos suma
Un array que contiene tanto strings como números no tiene ningún tipo de Go que lo describa. Los generadores que inventan uno producen código que falla al deserializar; interface{} es la respuesta honesta, y el lugar para arreglarlo es la API, no el struct.
La salida ya está alineada según gofmt
Nombres, tipos, etiquetas y comentarios finales se rellenan con espacios hasta columnas coincidentes. Sin eso, el primer gofmt en CI produce un diff en un archivo que nadie editó.
Ejemplo
[
{ "user_id": 900719925474099, "api_url": "https://x.dev" },
{ "user_id": 12, "api_url": "https://y.dev", "note": "hi" }
]Generado
type User struct {
UserID int64 `json:"user_id"`
APIURL string `json:"api_url"`
Note *string `json:"note,omitempty"` // optional
}¿Qué opciones y casos límite admite Convertir JSON a Go?
| Parámetro | Tipo | Valor predeterminado | Comportamiento y casos límite |
|---|---|---|---|
| Paquete | identificador | main | La cláusula package en la parte superior del archivo. Establécela en el paquete donde reside el archivo de destino — models, api, internal/store. |
| punteros | booleano | on | Los campos opcionales y nulos se convierten en *T, para que una clave ausente sea distinguible del valor cero. Desactivar esto hace que la struct sea más simple y hace que los datos faltantes sean invisibles. |
| omitempty | booleano | on | Añade ,omitempty a los campos opcionales para que desaparezcan cuando se vuelvan a serializar. Ten en cuenta que omitempty también elimina una cadena vacía genuina o un cero, y es por eso que el puntero importa. |
| json.Number | booleano | off | Emite json.Number para números no enteros en lugar de float64, preservando los dígitos literales. Vale la pena para dinero o para IDs que llegan como números sin comillas más allá del rango exacto de float64. |
| string | tipo de Go | string | Los formatos detectados (uuid, date-time, email, uri) se anotan en un comentario en lugar de mapearse a un tipo, porque encoding/json no analizará una marca de tiempo en time.Time sin un unmarshaller personalizado. |
| integer | tipo de Go | int / int64 | int por defecto; int64 cuando cualquier ejemplo supera 2,147,483,647. Un ID truncado es un error que solo aparece en producción. |
| number | tipo de Go | float64 | Cualquier valor con parte fraccionaria. Si un entero y un flotante aparecen en el mismo campo a través de registros, el campo se convierte en float64. |
| objeto anidado | tipo de Go | *Named | Se promueve a su propia struct y se referencia por puntero, lo que evita la copia en la asignación y permite que el campo sea nulo. Las formas idénticas comparten una struct. |
Preguntas frecuentes
¿Por qué cada campo va en mayúsculas si mis claves JSON van en minúsculas?
Porque encoding/json solo ve los campos exportados, y exportar en Go significa empezar con mayúscula. Un campo en minúsculas se salta tanto Marshal como Unmarshal sin dar error, así que el struct compila, se ejecuta y no hace nada. La etiqueta json lleva la clave original, así que el formato en la red no cambia.
¿Necesito los punteros?
Los necesitas allí donde ausente y cero significan cosas distintas. Un *int que es nil significa que la clave faltaba; un int que es 0 podría ser cualquiera de las dos cosas. Para un campo como retry_count o discount esa distinción normalmente importa. Para un campo que sabes que siempre está presente es ruido, y desactivar los punteros es razonable.
¿Por qué mi marca de tiempo es un string en lugar de time.Time?
Porque encoding/json no interpreta por su cuenta una marca de tiempo arbitraria como time.Time — solo maneja RFC 3339, y solo si el campo está declarado como time.Time, que entonces falla de forma rotunda con cualquier otro formato. Generar time.Time a partir del ejemplo produciría un struct que entra en pánico con el primer registro que formatee una fecha de otra manera. El formato detectado se indica en un comentario para que puedas tomar esa decisión a conciencia.
¿Qué hace un interface{} en mi struct?
Un campo contenía tipos realmente distintos entre tus ejemplos — un string en un registro, un objeto en otro — y Go no tiene un tipo que lo exprese. Las alternativas son un UnmarshalJSON personalizado, un json.RawMessage que descodificas después, o arreglar la API. Un tipo unión generado simplemente fallaría al deserializar.
¿gofmt cambiará la salida?
No debería. Los nombres de campo, los tipos, las etiquetas y los comentarios finales se rellenan hasta columnas coincidentes, que es justo lo que gofmt hace con un struct. Si encuentras un caso en el que gofmt aún produce un diff, es un bug que merece reportarse, no una preferencia de estilo.
¿Se sube mi JSON?
No. El análisis, la inferencia de tipos y el renderizado se ejecutan todos en tu navegador. No se envía nada a ningún sitio, lo cual importa porque los ejemplos que la gente pega en un generador de structs suelen ser respuestas reales de un sistema con el que se están integrando.