JSON (Javascript Object Notation) es un lenguaje de intercambio de datos liviano que está basado en descripción en texto. Sus ventajas incluyen ser auto descriptivo, fácil de entender, etc. Incluso aunque es un subconjunto de Javascript, JSON utiliza un formato distinto de texto. El resultado es que puede ser considerado un lenguaje independiente. JSON tiene una similaridad con la familia de los lenguajes C
La gran diferencia entre JSON y XML es que XML es un lenguaje de etiquetado, y JSON no lo es. JSON es mas pequeño y más rápido que XML, por eso es mucho mas rápido y fácil de analizar en navegadores, la cuel es una de las razones por la cual muchas plataformas prefieren usar JSON como su lenguaje de intercambio de datos.
Desde que JSON se ha convertido más y más importante en el desarrollo web, vamos a echarle un vistazo al soporte que le da Go a JSON. Encontrarás que la librería estándar de Go tiene muy buen soporte para codificar y decodificar JSON.
Aquí usamos JSON para representar el ejemplo de la sección anterior:
{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}
El resto de la sección usaremos este JSON para introducir los conceptos de JSON en Go.
Supon que tienes un el JSON del ejemplo anterior. ¿Cómo podemos analizar esta información y mapearla a una estructura en Go? Go provee las siguientes funciones para este propósito.
func Unmarshal(data []byte, v interface{}) error
Nosotros usar esta función como:
package main
import (
"encoding/json"
"fmt"
)
type Server struct {
ServerName string
ServerIP string
}
type Serverslice struct {
Servers []Server
}
func main() {
var s Serverslice
str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
json.Unmarshal([]byte(str), &s)
fmt.Println(s)
}
En el ejemplo de arriba, definimos una estructura correspondiente en Go para nuestro JSON, utilizando un segmento para un arreglo de objetos JSON y nombre de campos como nuestras llaves JSON. Pero ¿cómo Go sabe que objeto JSON corresponde a que campo específico? Supón que tenemos una llave llamada Foo
en JSON. ¿Cómo encontramos el campo correspondiente?
- Primero, Go intenta encontrar el campo exportado (en mayúscula) que contiene la etiqueta
Foo
. - Si ninguna coincidencia es encontrada, busca un campo cuyo nombre sea
Foo
- Si todavía no hay coincidencias, busca por algo como
Foo
oFoO
, ignorando si es mayúscula o minúscula.
Puedes haber notado que todos los campos que van a ser asignados deberían ser exportados, y Go solamente asigna campos que pueden ser encontrados, ignorando todos los demás. Esto puede ser útil si tu quieres lidiar con pedazos grande de JSON pero solamete usar un subconjunto específico de él; la información que no necesitas puede ser fácilmente descartada.
Cuando sabemos que tipo de JSON esperar por adelantado, nosotros podemos analizarlo a una estructura específica. pero ¿Si no sabemos?
Sabemos que una interfaz {} puede ser cualquier cosa en Go, así que es el mejor contenedor para guardar nuestro JSON con un formato desconocido. El paquete JSON usa map[String]interface{}
y []interface
para guardar todos los tipos de objetos JSON y arreglos. Aquí está una lista de las relaciones de mapeo de JSON:
bool
representanbooleanos JSON
,float64
representannúmeros JSON
,string
representancadenas JSON
nil
representanJSON null
.
Supón que tienes la siguiente información JSON:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
Ahora analizamos este JSON a una interface{}:
var f interface{}
err := json.Unmarshal(b, &f)
f
almacena un mapa, donde las llaves osn strings y los valores son interfaces.
f = map[string]interface{}{
"Name": "Wednesday",
"Age": 6,
"Parents": []interface{}{
"Gomez",
"Morticia",
},
}
Entonces, ¿cómo accedemos a la información? Aserción de tipos.
m := f.(map[string]interface{})
Después de la aserión, puede usar el siguiente código para acceder a la información:
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case int:
fmt.Println(k, "is int", vv)
case float64:
fmt.Println(k,"is float64",vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}
Como puedes ver, podemos analizar el JSON como un formato anónimo a través de interfaces y aserción de tipos.
El ejemplo superior es la solución oficial, pero la aserción de tipos no siempre es conveniente. Entonces, recomedo un proyecto de código abierto llamado simplejson
, creado y mantenido por bitly. Aquí hay un ejemplo de cómo usarlo en un proyecto con JSON de formatos desconocidos:
js, err := NewJson([]byte(`{
"test": {
"array": [1, "2", 3],
"int": 10,
"float": 5.150,
"bignum": 9223372036854775807,
"string": "simplejson",
"bool": true
}
}`))
arr, _ := js.Get("test").Get("array").Array()
i, _ := js.Get("test").Get("int").Int()
ms := js.Get("test").Get("string").MustString()
No es difícil ver que conveniente es esto. Revisa el repositorio para ver mas información: https://github.com/bitly/go-simplejson.
En muchas situaciones, necesitamos producir información JSON y responder a los clientes. En Go, el paquete JSON tiene una función llamada Marshal
para hacer justamente esto:
func Marshal(v interface{}) ([]byte, error)
Supón que necesitas producir una lista de información de servidores. Tenemos el siguiente ejemplo:
package main
import (
"encoding/json"
"fmt"
)
type Server struct {
ServerName string
ServerIP string
}
type Serverslice struct {
Servers []Server
}
func main() {
var s Serverslice
s.Servers = append(s.Servers, Server{ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1"})
s.Servers = append(s.Servers, Server{ServerName: "Beijing_VPN", ServerIP: "127.0.0.2"})
b, err := json.Marshal(s)
if err != nil {
fmt.Println("json err:", err)
}
fmt.Println(string(b))
}
Salida:
{"Servers":[{"ServerName":"Shanghai_VPN","ServerIP":"127.0.0.1"},{"ServerName":"Beijing_VPN","ServerIP":"127.0.0.2"}]}
Como sabes, todos los nombres de campo tienen la primera letra en mayúscula, pero si quieres ver los campos de tu JSON con la primera en minúscula, deberías usar las etiquetas de estructura
. De otra manera no producirá información para los campos privados.
type Server struct {
ServerName string `json:"serverName"`
ServerIP string `json:"serverIP"`
}
type Serverslice struct {
Servers []Server `json:"servers"`
}
Después de esta modificación, podemos producir la misma información JSON que antes.
Aquí hay algunos puntos que necesitas tener en mente cuando tratas de producir JSON:
- Etiquetas de campos que contengan
"-"
no estarán en la salida. - Si una etiqueta contiene un nombre personalizado, Go usará esto en lugar del nombre del campo, como
serverName
en el ejemplo superior. - Si una etiqueta contiene
omitempty
, este campo no estará en la salida si tiene un valor 0. - Si el campo es de tipo booleano, cadena, entero, etc, y su etiqueta contiene
",string"
, Go convertirá este campo en su tipo correspondiente de JSON.
Ejemplo:
type Server struct {
// ID will not be outputed.
ID int `json:"-"`
// ServerName2 will be converted to JSON type.
ServerName string `json:"serverName"`
ServerName2 string `json:"serverName2,string"`
// If ServerIP is empty, it will not be outputted.
ServerIP string `json:"serverIP,omitempty"`
}
s := Server {
ID: 3,
ServerName: `Go "1.0" `,
ServerName2: `Go "1.0" `,
ServerIP: ``,
}
b, _ := json.Marshal(s)
os.Stdout.Write(b)
Salida:
{"serverName":"Go \"1.0\" ","serverName2":"\"Go \\\"1.0\\\" \""}
La función Marshal
solamente retorna información cuando termina exitosamente, entonces aquí hay algunos puntos para tener en cuenta:
- JSON únicamente soporta cadenas como llaves, entonces si quieres codificar un mapa, deber ser de tipo
map[string]T
, dondeT
es el tipo en Go. - Tipos como canales, tipos complejos y funciones no pueden ser codificadas en JSON.
- No trates de codificar información cíclica, llevará a una reecursión infinita.
- Si el campo es un puntero, Go mostrará la información a la que apunta, o sino mostrará null si apunta a nil.
En esta sección, introducimos como decodificar y codificar información JSON en Go. También miramos a un proyecto de terceros llamado simplejson
que es útil para analizar JSON de formato desconocido. Estos son todos los conceptos útilos para desarrolloar aplicaciones web en Go.
- Índice
- Sección previa: XML
- Siguiente sección: Expresiones regulares