I was really surprised how easy it was to read an XML document using the encoding/xml package that comes with the standard library. The package works by defining structs that map the XML document. If you need more flexibility then use Gustavo Niemeyer’s xmlpath package (found here).

Here is the XML document we are going to read and de-serialize:

<straps>
    <strap key="CompanyName" value="NEWCO" />
    <strap key="UseEmail" value="true" />
</straps>

The first thing we need to do is define the structs we will use to map the document:

type XMLStrap struct {
    XMLName  xml.Name ̀ xml:"strap"̀
    Key      string   ̀ xml:"key,attr"̀
    Value    string   ̀ xml:"value,attr"̀
}

type XMLStraps struct {
    XMLName  xml.Name    ̀ xml:"straps"̀
    Straps   []XMLStrap ̀ xml:"strap"̀
}

There are two structs, one for the entire document (<straps>) and one for each individual child node (<strap>). If you look closely at the structs you may see something new. Each field has a tag associated with it. These tags are bound to each individual field. Go's reflect package allows you to access these tags.

These tag formats are specific to the decoding support inside the encoding/xml package. The tags map the nodes and attributes of the XML document to the struct.

The following code decodes the XML document and returns the array of strap nodes:

func ReadStraps(reader io.Reader) ([]XMLStrap, error) {
    var xmlStraps XMLStraps
    if err := xml.NewDecoder(reader).Decode(&xmlStraps); err != nil {
        return nil, err
    }

    return xmlStraps.Straps, nil
}

The function takes an io.Reader. We will be passing a os.File variable into this method. The function returns an array of pointers for each strap we read from the file.

First we create a XMLStraps variable and get its address. Next we create a decoder using the xml.NewDecoder method passing the io.Reader object. Then we call Decode which reads the file and de-serializes the file into the XMLStraps variable. Then we just return the array of strap values.

The following completes the sample code:

/*
straps.xml should be located in the default working directory

<straps>
    <strap key="CompanyName" value="NEWCO" />
    <strap key="UseEmail" value="true" />
</straps>
*/
package main

import (
    "encoding/xml"
    "fmt"
    "io"
    "os"
    "path/filepath"
)

type XMLStrap struct {
    XMLName  xml.Name ̀ xml:"strap"̀
    Key      string   ̀ xml:"key,attr"̀
    Value    string   ̀ xml:"value,attr"̀
}

type XMLStraps struct {
    XMLName  xml.Name    ̀ xml:"straps"̀
    Straps   []XMLStrap ̀ xml:"strap"̀
}

func ReadStraps(reader io.Reader) ([]XMLStrap, error) {
    var xmlStraps XMLStraps
    if err := xml.NewDecoder(reader).Decode(&xmlStraps); err != nil {
        return nil, err
    }

    return xmlStraps.Straps, nil
}

func main() {
    // Build the location of the straps.xml file
    // filepath.Abs appends the file name to the default working directly
    strapsFilePath, err := filepath.Abs("straps.xml")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    // Open the straps.xml file
    file, err := os.Open(strapsFilePath)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    defer file.Close()

    // Read the straps file
    xmlStraps, err := ReadStraps(file)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    // Display The first strap
    fmt.Printf("Key: %s  Value: %s", xmlStraps[0].Key, xmlStraps[0].Value)
}

I hope this sample gets you started with reading XML documents for your Go applications.

Trusted by top technology companies

We've built our reputation as educators and bring that mentality to every project. When you partner with us, your team will learn best practices and grow along the way.

30,000+

Engineers Trained

1,000+

Companies Worldwide

12+

Years in Business