Skip to content

Latest commit

 

History

History
231 lines (177 loc) · 10.5 KB

File metadata and controls

231 lines (177 loc) · 10.5 KB

7.1 XML

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.

Analizar XML (Parse XML)

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, como Description en el ejemplo de arriba:

    Shanghai_VPN127.0.0.1Beijing_VPN127.0.0.2

  • Si un campo llamadoXMLName y su tipo es xml.Name , entonces se pone el nombre del elemento, como servers 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 y serverip 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.

Producir XML

¿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 xml:"name>first" LastName string xml:"name>last"

    Asta Xie

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.

Enlaces