XML
suggest changePackage 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)(0xc00005a400), 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 (<state>CA</state>
) or attribute (<person age="34">
).
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:
city
element was not present in XML- element was present but had empty value (
<city></city>
)
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>