XML es un formato de comunicación de datos comúnmente utilizado en los servicios web hoy en día, se hace más y más importante el papel en el desarrollo diario. En esta sección, vamos a presentar la forma de trabajar con XML a través de la biblioteca estándar.
No voy a enseñar lo que es XML o algo así, por favor lea más documentación acerca de XML si es que no sabe lo que es. Sólo nos centraremos en cómo codificar y decodificar archivos XML.
Suponga que usted es de el personal operaciones, y dispone de los siguientes archivos de configuración XML:
<?xml version="1.0" encoding="utf-8"?>
<servers version="1">
<server>
<serverName>Shanghai_VPN</serverName>
<serverIP>127.0.0.1</serverIP>
</server>
<server>
<serverName>Beijing_VPN</serverName>
<serverIP>127.0.0.2</serverIP>
</server>
</servers>
Este documento XML contiene dos tipos de información acerca de su servidor, que son el nombre del servidor y la IP; vamos a utilizar este documento en nuestros siguientes ejemplos.
Cómo analizamos este documento XML? Podemos utilizar la función Unmarshal
del paquete xml
para hacer esto.
func Unmarshal(data []byte, v interface{}) error
data recibe el stream de datos del XML, v es la estructura que desea para la salida, que es una interfaz, lo que significa que puede convertir el XML a cualquier tipo de estructuras. Aquí sólo hablamos acerca de cómo convertirla a un struct
porque tienen estructuras de árboles similares.
Código de ejemplo:
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
)
type Recurlyservers struct {
XMLName xml.Name `xml:"servers"`
Version string `xml:"version,attr"`
Svs []server `xml:"server"`
Description string `xml:",innerxml"`
}
type server struct {
XMLName xml.Name `xml:"server"`
ServerName string `xml:"serverName"`
ServerIP string `xml:"serverIP"`
}
func main() {
file, err := os.Open("servers.xml") // For read access.
if err != nil {
fmt.Printf("error: %v", err)
return
}
defer file.Close()
data, err := ioutil.ReadAll(file)
if err != nil {
fmt.Printf("error: %v", err)
return
}
v := Recurlyservers{}
err = xml.Unmarshal(data, &v)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Println(v)
}
XML actualmente es una estructura de datos de árbol, y podemos definir casi una misma estructura en Go, entonces vamo a utilizar xml.Unmarshal
para convertir de XML a nuestro objeto struct. El código de ejemplo imprimirá siguiente contenido:
{{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} Beijing_VPN 127.0.0.2}]
<server>
<serverName>Shanghai_VPN</serverName>
<serverIP>127.0.0.1</serverIP>
</server>
<server>
<serverName>Beijing_VPN</serverName>
<serverIP>127.0.0.2</serverIP>
</server>
}
Utilizamos xml.Unmarshal
para pasar el documento XML correspondiente a un objeto struct, y usted debería ver que tenemos algo así como xml:"serverName"
en nuestra estructura. Esta es una característica de la estructura que es llamada struct tag
para ayudar a la reflexión. Veamos la definición de Unmarshal
de nuevo:
func Unmarshal(data []byte, v interface{}) error
El primer argumento es la secuencia de datos XML, el segundo argumento es el tipo de almacenamiento, por ahora soporta struct, slice y string. El paquete XML utiliza la reflexión para lograr la asignación de datos, por lo que todos los campos de v deben ser exportados. Pero todavía tenemos un problema, ¿cómo puede saber qué campo se corresponde con cual otro? Aqui hay un nivel de prioridad cuando se realiza el parse de los datos. Se trata de encontrar el struct tag en primer lugar, si no se puede encontrar, se intenta obtener el nombre del campo. Tenga en cuenta que todas las etiquetas, el nombre del campo y el elemento XML distinguen entre mayúsculas y minúsculas, por lo que usted tiene que asegurarse de que hay una correspondencia uno a uno.
El mecanismo de reflexión de go le permite utilizar estos datos de la etiqueta para reflejar los datos del XML al objeto struct. Si usted quiere saber más sobre la reflexión en Go, por favor, lea más acerca de la documentación del paquete de struct tag y reflect.
Aquí están las reglas del paquete xml
cuando realiza el análisis sintáctico de un XML a un struct:
-
Si el tipo de campo es string or []byte con el tag
",innerxml"
,Unmarshal
asignara los datos del XML sin formato a el, comoDescription
en el ejemplo de arriba:Shanghai_VPN127.0.0.1Beijing_VPN127.0.0.2
-
Si un campo llamado
XMLName
y su tipo esxml.Name
, entonces se pone el nombre del elemento, comoservers
del ejemplo anterior. -
Si la etiqueta de un campo contiene el nombre del elemento correspondiente, entonces se pone el nombre del elemento, así como
servername
yserverip
en el ejemplo anterior. -
Si la etiqueta de un campo contiene ", attr" , entonces obtiene el atributo correspondiente del elemento, al igual que
version
en el ejemplo anterior. -
Si la etiqueta de un campo contiene algo así como "a> b> c" , se obtiene el valor del elemento c del nodo b del nodo a.
-
Si la etiqueta de un campo contiene un "=" , entonces no se pone nada.
-
Si la etiqueta de un campo contiene ",any" , entonce toma todos los elementos hijos que no se ajusten a otras normas.
-
Si los elementos XML tienen uno o más comentarios, todos estos comentarios se agregarán al primer campo que tiene la etiqueta que contiene
",comments"
, este tipo de campo puede ser de tipo string or []byte, si este tipo de campo no existe, todos los comentarios se descartan.
Estas reglas nos indican cómo definir las etiquetas de la estructura, una vez que entiendan estas reglas, todo es fácil como el código de ejemplo. Dado que las etiquetas y los elementos del XML son correspondencia uno a uno, también podemos utilizar listas para representar múltiples elementos en un mismo nivel.
Tenga en cuenta que todos los campos del struct deben exportarse (comenzar con mayusculas) con el fin de analizar los datos correctamente.
¿Qué pasa si queremos producir el documento XML en lugar de analizarlo, ¿cómo podemos hacerlo en Go? el paquete xml proporciona dos funciones que son Marshal
and MarshalIndent
donde la segunda función tiene indentacion para el documento XML. Su definición es de la siguiente manera:
func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
El primer argumento es para almacenar la secuencia de datos XML para ambas funciones.
Vamos a ver un ejemplo para ver cómo funciona:
package main
import (
"encoding/xml"
"fmt"
"os"
)
type Servers struct {
XMLName xml.Name `xml:"servers"`
Version string `xml:"version,attr"`
Svs []server `xml:"server"`
}
type server struct {
ServerName string `xml:"serverName"`
ServerIP string `xml:"serverIP"`
}
func main() {
v := &Servers{Version: "1"}
v.Svs = append(v.Svs, server{"Shanghai_VPN", "127.0.0.1"})
v.Svs = append(v.Svs, server{"Beijing_VPN", "127.0.0.2"})
output, err := xml.MarshalIndent(v, " ", " ")
if err != nil {
fmt.Printf("error: %v\n", err)
}
os.Stdout.Write([]byte(xml.Header))
os.Stdout.Write(output)
}
El ejemplo anterior imprime la siguiente información:
<?xml version="1.0" encoding="UTF-8"?>
<servers version="1">
<server>
<serverName>Shanghai_VPN</serverName>
<serverIP>127.0.0.1</serverIP>
</server>
<server>
<serverName>Beijing_VPN</serverName>
<serverIP>127.0.0.2</serverIP>
</server>
</servers>
Como hemos definido antes, la razón por la que tenemos os.Stdout.Write([]byte(xml.Header))
son dos la función xml.MarshalIndent
y xml.Marshal
no se crea la cabecera del XML por sí misma, así que tenemos que imprimirlo en orden para producir el documento XML correctamente.
Aquí vemos que Marshal
recibe a v que es del tipo interface{}
, entonces cuales son las reglas cuando se produce un documento XML?
- Si v es un array o lista, imprime todos los elementos como el valor.
- Si v es un puntero, se imprime el contenido al que v señale, no imprime nada cuando v es nil.
- Si v es una interfaz, va a lidiar con la interfaz también
- Si v es uno de los otros tipos, se imprime valor de ese tipo.
Así que ¿cómo puede decidir el nombre elementos? Para ello se sigue las reglas siguientes:
- Si v es una estructura, que define el nombre en la etiqueta de XMLName.
- El nombre del campo es XMLName y el tipo es xml.Name.
- Etiqueta en los campos de una estructura.
- Nombre del campo en la estructura.
- Nombre del Tipo con el que va a realizar la conversion.
Entonces tenemos que encontrar la manera de fijar las etiquetas con el fin de producir el documento XML final.
-
XMLName no se imprimirá.
-
Los campos que tienen etiqueta que contienen "-" no se imprimirán.
-
Si la etiqueta contiene
"name,attr"
, se utiliza el nombre como nombre de atributo y el valor del campo como valor, al igual que la versión en el ejemplo anterior. -
Si la etiqueta contiene
",attr"
, utiliza el nombre del campo como nombre de atributo y el valor del campo como valor. -
Si la etiqueta contiene
",chardata"
, imprime los datos de caracteres en lugar del elemento. -
Si la etiqueta contiene
",innerxml"
, imprime el valor crudo. -
Si la etiqueta contiene
",comment"
, se lo imprime como comentarios sin escapar, por lo que no puede tener "-" en su valor. -
Si la etiqueta contiene
"omitempty"
, omite este campo si su valor es de valor cero, incluyendo falsa, 0, un puntero nulo o una interfaz nula, un array de 0 elementos, listas, map y string. -
Si la etiqueta contiene
"a>b>c"
, imprime los tres elementos en los que A contiene b, b contiene c, al igual que el siguiente código:FirstName string
Asta Xiexml:"name>first"
LastName stringxml:"name>last"
Usted puede notar que el código del struct es muy útil cuando se trabaja con XML, así como otro formato de datos, en las secciones siguientes, si usted todavía tiene problemas con el trabajo con los struct tag, probablemente debería leer más documentación al respecto antes de entrar en la siguiente sección.
- Indice
- Sección anterior: Archivos de texto
- Siguiente sección: JSON