Go by Example

XML

encoding/xml Marshal and Unmarshal, struct tags for element and attribute mapping, streaming with Encoder/Decoder, and handling namespaces.

The encoding/xml package encodes Go values to XML and decodes XML into Go values. Struct tags (xml:"name") control how Go fields map to XML elements and attributes.

Encode a struct to XML with xml.Marshal. The xml.Header constant provides the standard XML declaration. Use xml:",attr" to map a field to an XML attribute instead of a child element.

package main
 
import (
    "encoding/xml"
    "fmt"
)
 
type Book struct {
    XMLName xml.Name `xml:"book"`
    ID      int      `xml:"id,attr"`
    Title   string   `xml:"title"`
    Author  string   `xml:"author"`
}
 
func main() {
    b := Book{ID: 1, Title: "The Go Programming Language", Author: "Donovan"}
    out, err := xml.MarshalIndent(b, "", "  ")
    if err != nil {
        panic(err)
    }
    fmt.Println(xml.Header + string(out))
}

Decode XML into a struct with xml.Unmarshal. Fields without a tag use their lowercased name. Unknown elements are silently ignored by default.

package main
 
import (
    "encoding/xml"
    "fmt"
)
 
type Book struct {
    XMLName xml.Name `xml:"book"`
    ID      int      `xml:"id,attr"`
    Title   string   `xml:"title"`
    Author  string   `xml:"author"`
}
 
func main() {
    data := []byte(`<book id="42"><title>The Go Programming Language</title><author>Donovan</author></book>`)
 
    var b Book
    if err := xml.Unmarshal(data, &b); err != nil {
        panic(err)
    }
    fmt.Printf("ID:%d Title:%s Author:%s\n", b.ID, b.Title, b.Author)
}

Use xml.Decoder for streaming. It reads tokens one at a time and avoids loading the full document into memory - essential for large XML files or feeds.

package main
 
import (
    "encoding/xml"
    "fmt"
    "strings"
)
 
type Item struct {
    Title string `xml:"title"`
}
 
func main() {
    feed := `<feed><item><title>First</title></item><item><title>Second</title></item></feed>`
 
    dec := xml.NewDecoder(strings.NewReader(feed))
    for {
        tok, err := dec.Token()
        if err != nil {
            break // io.EOF
        }
        if se, ok := tok.(xml.StartElement); ok && se.Name.Local == "item" {
            var item Item
            dec.DecodeElement(&item, &se)
            fmt.Println(item.Title)
        }
    }
}

In production

XML is common in enterprise integrations, SOAP services, and RSS/Atom feeds. encoding/xml handles straightforward cases well, but for large XML streams use xml.Decoder token-by-token to avoid loading the entire document into memory - a 500 MB XML export will OOM your service if passed to xml.Unmarshal. For namespace-heavy XML (SOAP envelopes, XHTML), embed xml.Name with the Space field to match qualified names precisely. When generating XML for third-party consumers, always test the output against their schema validator - whitespace, attribute ordering, and namespace prefixes can all cause rejections even when the data is structurally correct.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free