Go encoding/xml

SerializationGoXMLStandard LibraryStructured Data

Library

encoding/xml - Go Language Standard XML Encoding/Decoding Package

Overview

encoding/xml is an XML serialization package included in Go's standard library. It provides comprehensive functionality for reading and writing XML data, including flexible mapping using struct tags, namespace support, and streaming processing. Supporting various XML processing patterns such as generating and parsing XML documents, DOM manipulation, and SAX-style streaming processing, it is well-suited for handling XML as a web service format, configuration file format, or data exchange format.

Details

The encoding/xml package provides a complete feature set for handling XML in Go. By tagging struct fields, you can declaratively define mappings between XML elements/attributes and struct fields. This design allows intuitive mapping of even complex XML structures to Go's type system.

The package supports both batch processing via Marshal/Unmarshal functions and streaming processing via Encoder/Decoder. For large XML files, the Decoder's token-based processing enables memory-efficient handling. Additionally, by implementing xml.Marshaler and xml.Unmarshaler interfaces, you can define custom serialization logic.

Important XML specification features such as XML namespaces, CDATA, comments, and processing instructions are also supported. However, advanced features like DTD validation or XSD schema validation are not included, so external libraries are needed for such functionality.

Advantages and Disadvantages

Advantages

  • Standard Library: XML processing without external dependencies
  • Flexible Mapping: Declarative XML-Go type mapping via struct tags
  • Streaming Support: Efficient processing of large XML files
  • Namespace Support: Proper handling of XML namespaces
  • Customizable: Extension through Marshaler/Unmarshaler interfaces
  • Type Safety: Safety through compile-time type checking

Disadvantages

  • No Schema Validation: DTD and XSD validation not supported
  • No XPath Support: No query functionality via XPath expressions
  • No XSLT Support: No XSL transformation capabilities
  • Performance: May be slower than specialized XML libraries
  • Error Messages: XML parse error messages can be unclear
  • Completeness: Some XML specifications (external entities, etc.) unsupported

References

Code Examples

1. Basic XML Encode and Decode

package main

import (
    "encoding/xml"
    "fmt"
    "log"
)

type Person struct {
    XMLName xml.Name `xml:"person"`
    Name    string   `xml:"name"`
    Age     int      `xml:"age,attr"`
    Email   string   `xml:"contact>email"`
    Phone   string   `xml:"contact>phone"`
}

func main() {
    // Encode
    person := Person{
        Name:  "John Doe",
        Age:   30,
        Email: "[email protected]",
        Phone: "555-1234",
    }
    
    xmlData, err := xml.MarshalIndent(person, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("XML Output:")
    fmt.Println(xml.Header + string(xmlData))
    
    // Decode
    var decoded Person
    err = xml.Unmarshal(xmlData, &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("\nDecoded: %+v\n", decoded)
}

2. Complex Structures and Lists

package main

import (
    "encoding/xml"
    "fmt"
)

type Library struct {
    XMLName xml.Name `xml:"library"`
    Name    string   `xml:"name,attr"`
    Books   []Book   `xml:"books>book"`
}

type Book struct {
    ISBN      string   `xml:"isbn,attr"`
    Title     string   `xml:"title"`
    Authors   []string `xml:"authors>author"`
    Publisher string   `xml:"publisher"`
    Year      int      `xml:"year"`
    Price     float64  `xml:"price"`
    Currency  string   `xml:"price,attr,currency"`
}

func complexStructureExample() {
    library := Library{
        Name: "Tech Library",
        Books: []Book{
            {
                ISBN:      "978-0-123456-78-9",
                Title:     "Go Programming Language",
                Authors:   []string{"Alan Donovan", "Brian Kernighan"},
                Publisher: "Tech Press",
                Year:      2023,
                Price:     49.99,
                Currency:  "USD",
            },
            {
                ISBN:      "978-0-987654-32-1",
                Title:     "Practical XML Processing",
                Authors:   []string{"Jane Smith"},
                Publisher: "Data Publishing",
                Year:      2022,
                Price:     39.99,
                Currency:  "USD",
            },
        },
    }
    
    xmlData, _ := xml.MarshalIndent(library, "", "  ")
    fmt.Println(xml.Header + string(xmlData))
}

3. Namespace Handling

package main

import (
    "encoding/xml"
    "fmt"
)

type Envelope struct {
    XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
    Body    Body     `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type Body struct {
    GetWeather GetWeatherRequest `xml:"http://weather.example.com GetWeather"`
}

type GetWeatherRequest struct {
    City        string `xml:"city"`
    CountryCode string `xml:"countryCode"`
}

func namespaceExample() {
    request := Envelope{
        Body: Body{
            GetWeather: GetWeatherRequest{
                City:        "New York",
                CountryCode: "US",
            },
        },
    }
    
    xmlData, _ := xml.MarshalIndent(request, "", "  ")
    fmt.Println(string(xmlData))
}

4. Streaming Processing (Large XML Files)

package main

import (
    "encoding/xml"
    "fmt"
    "io"
    "strings"
)

func streamingExample() {
    xmlData := `
    <products>
        <product id="1"><name>Product A</name><price>100</price></product>
        <product id="2"><name>Product B</name><price>200</price></product>
        <product id="3"><name>Product C</name><price>300</price></product>
    </products>`
    
    decoder := xml.NewDecoder(strings.NewReader(xmlData))
    
    var inProduct bool
    var currentProduct struct {
        ID    string
        Name  string
        Price string
    }
    
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            return
        }
        
        switch t := token.(type) {
        case xml.StartElement:
            if t.Name.Local == "product" {
                inProduct = true
                for _, attr := range t.Attr {
                    if attr.Name.Local == "id" {
                        currentProduct.ID = attr.Value
                    }
                }
            } else if inProduct {
                var value string
                decoder.DecodeElement(&value, &t)
                switch t.Name.Local {
                case "name":
                    currentProduct.Name = value
                case "price":
                    currentProduct.Price = value
                }
            }
        case xml.EndElement:
            if t.Name.Local == "product" {
                fmt.Printf("Product: ID=%s, Name=%s, Price=$%s\n",
                    currentProduct.ID, currentProduct.Name, currentProduct.Price)
                currentProduct = struct{ ID, Name, Price string }{}
                inProduct = false
            }
        }
    }
}

5. Custom Marshaling

package main

import (
    "encoding/xml"
    "fmt"
    "time"
)

type DateTime struct {
    time.Time
}

// MarshalXML implements xml.Marshaler
func (dt DateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    dateStr := dt.Format("2006-01-02T15:04:05")
    return e.EncodeElement(dateStr, start)
}

// UnmarshalXML implements xml.Unmarshaler
func (dt *DateTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var dateStr string
    if err := d.DecodeElement(&dateStr, &start); err != nil {
        return err
    }
    
    parsed, err := time.Parse("2006-01-02T15:04:05", dateStr)
    if err != nil {
        return err
    }
    
    dt.Time = parsed
    return nil
}

type Event struct {
    XMLName   xml.Name `xml:"event"`
    Name      string   `xml:"name"`
    StartTime DateTime `xml:"startTime"`
    EndTime   DateTime `xml:"endTime"`
}

func customMarshalingExample() {
    event := Event{
        Name:      "Tech Conference",
        StartTime: DateTime{time.Now()},
        EndTime:   DateTime{time.Now().Add(2 * time.Hour)},
    }
    
    xmlData, _ := xml.MarshalIndent(event, "", "  ")
    fmt.Println(string(xmlData))
    
    // Decode test
    var decoded Event
    xml.Unmarshal(xmlData, &decoded)
    fmt.Printf("Event: %s\nStart: %v\nEnd: %v\n",
        decoded.Name,
        decoded.StartTime.Format("2006/01/02 15:04"),
        decoded.EndTime.Format("2006/01/02 15:04"))
}

6. XML and JSON Conversion

package main

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
)

type Product struct {
    XMLName     xml.Name `xml:"product" json:"-"`
    ID          int      `xml:"id,attr" json:"id"`
    Name        string   `xml:"name" json:"name"`
    Description string   `xml:"description" json:"description"`
    Price       float64  `xml:"price" json:"price"`
    InStock     bool     `xml:"inStock" json:"in_stock"`
}

func xmlJsonConversion() {
    // Original data
    product := Product{
        ID:          123,
        Name:        "High-Performance Laptop",
        Description: "Latest CPU and GPU",
        Price:       1299.99,
        InStock:     true,
    }
    
    // Convert to XML
    xmlData, _ := xml.MarshalIndent(product, "", "  ")
    fmt.Println("XML Format:")
    fmt.Println(string(xmlData))
    
    // Convert to JSON
    jsonData, _ := json.MarshalIndent(product, "", "  ")
    fmt.Println("\nJSON Format:")
    fmt.Println(string(jsonData))
    
    // XML to JSON conversion
    var fromXML Product
    xml.Unmarshal(xmlData, &fromXML)
    jsonFromXML, _ := json.MarshalIndent(fromXML, "", "  ")
    fmt.Println("\nXML→JSON Conversion:")
    fmt.Println(string(jsonFromXML))
}