Essential Go  Suggest an edit

XML

Package encoding/xml in standard library provides functionality for serializing data as XML and parsing XML.

Parse XML into a struct

Parsing XML is similar to parsing JSON. You define structures that map to the structure of XML and unmarshal from []byte slice or io.Reader into a struct.

var xmlStr = `
<people>
	<person age="34">
		<first-name>John</first-name>
		<address>
			<city>San Francisco</city>
			<state>CA</state>
		</address>
	</person>

	<person age="23">
		<first-name>Julia</first-name>
	</person>
</people>`

type People struct {
	Person []Person `xml:"person"`
}

type Person struct {
	Age       int     `xml:"age,attr"`
	FirstName string  `xml:"first-name"`
	Address   Address `xml:"address"`
}

type Address struct {
	City  *string `xml:"city"`
	State string  `xml:"state"`
}

var people People
data := []byte(xmlStr)
err := xml.Unmarshal(data, &people)
if err != nil {
	log.Fatalf("xml.Unmarshal failed with '%s'\n", err)
}
fmt.Printf("%#v\n\n", people)
main.People{Person:[]main.Person{main.Person{Age:34, FirstName:"John", Address:main.Address{City:(*string)(0xc00000e3f0), State:"CA"}}, main.Person{Age:23, FirstName:"Julia", Address:main.Address{City:(*string)(nil), State:""}}}}

Unlike with serializing, when unmarshalling into a struct, we must pass a pointer to struct. Otherwise xml.Unmarshal will receive and modify a copy of the struct, not the struct itself. The copy will be then discarded after returning from xml.Unmarshal.

In XML a value can be represented as element (CA) or attribute ().

In xml struct tags element is the default. To switch to attribute, add ,attr to struct xml tag as done for Age.

All struct fields are optional. When not present in XML text their values will be untouched. When decoding into newly initialized struct their value will be zero value for a given type.

Field City shows that XML decoder can automatically decode into a pointer to a value.

This is useful when you need to know if a value was present in XML or not. If we used string for City field, we wouldn’t know if empty string means:

By using a pointer to a string we know that nil means there was no value.

Serialize a struct as XML

type People struct {
	XMLName        xml.Name `xml:"people"`
	Person         []Person `xml:"person"`
	noteSerialized int
}

type Person struct {
	Age       int     `xml:"age,attr"`
	FirstName string  `xml:"first-name"`
	Address   Address `xml:"address"`
}

type Address struct {
	City  string `xml:"city"`
	State string `xml:"state"`
}

people := People{
	Person: []Person{
		Person{
			Age:       34,
			FirstName: "John",
			Address:   Address{City: "San Francisco", State: "CA"},
		},
	},
	noteSerialized: 8,
}
d, err := xml.Marshal(&people)
if err != nil {
	log.Fatalf("xml.Marshal failed with '%s'\n", err)
}
fmt.Printf("Compact XML: %s\n\n", string(d))

d, err = xml.MarshalIndent(&people, "", "  ")
if err != nil {
	log.Fatalf("xml.MarshalIndent failed with '%s'\n", err)
}
fmt.Printf("Pretty printed XML:\n%s\n", string(d))
Compact XML: <people><person age="34"><first-name>John</first-name><address><city>San Francisco</city><state>CA</state></address></person></people>

Pretty printed XML:
<people>
  <person age="34">
    <first-name>John</first-name>
    <address>
      <city>San Francisco</city>
      <state>CA</state>
    </address>
  </person>
</people>

Both xml.Marshal and xml.MarshalIndent take interface{} as first argument. We can pass any Go value and it’ll be wrapped into interface{} with their type.

Marshaller will use reflection to inspect passed value and encode it as XML strings.

When serializing structs, only exported fields (whose names start with capital letter) are serialized / deserialized.

In our example, noteSerialized is not serialized.

Structs are serialized as XML elements.

Simple types can be serialized as XML elements or attributes (Age field in Person struct)

By default name of element / attribute is the same as name of the field.

We can provide custom mappings with struct tags.

xml:"city" tells XML encoder to use name city for field City.

When serializing structs, passing the value or a pointer to xml.Marshal generates the same result.

Passing a pointer is more efficient because passing by value creates unnecessary copy.

xml.MarshallIndent allows for pretty-printing of nested structures. The result takes up more space but is easier to read.

XMLName allows to control the name of top-level element. In our example, without providing XML element, the data would be serialized based on the struct name:

<People>
    ....
</People>

  ↑ ↓ to navigate     ↵ to select     Esc to close