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.