Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

encoding/xml - XML 编解码

概述

encoding/xml 包提供了 XML(Extensible Markup Language)数据的编码和解码功能。

XML 是什么

  • 📦 可扩展标记语言:用于存储和传输数据的标记语言
  • 🔧 自描述格式:数据包含结构信息
  • 📋 人类可读:文本格式,易于阅读和理解
  • 🛠️ 跨平台支持:广泛用于 Web 服务、配置文件

主要用途

  • 🌐 Web Services:SOAP API、XML-RPC
  • 📧 数据交换:系统间的数据传输
  • 🔐 配置文件:应用程序配置存储
  • 📊 文档格式:RSS、Atom、SVG 等
  • 🖼️ 数据持久化:结构化数据存储
  • 🔑 数字签名:XML Signature、XML Encryption

重要说明

  • ⚠️ 严格语法:XML 语法要求严格(标签必须闭合)
  • ⚠️ 命名空间:支持 XML Namespaces
  • ⚠️ 属性支持:支持 XML 属性
  • ⚠️ 字符编码:支持多种字符编码
  • 标准库支持:Go 标准库提供完整支持
  • 流式处理:支持 Token 流式解析
  • 结构体标签:使用 struct tag 自定义映射

XML 示例

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <name>John Doe</name>
    <age>30</age>
    <email>john@example.com</email>
    <address city="New York" zip="10001">
        <street>123 Main St</street>
    </address>
</person>

XML 基础

XML 语法规则

基本规则

  1. 必须有根元素
  2. 标签必须闭合
  3. 标签区分大小写
  4. 属性值必须用引号包围
  5. 特殊字符需要转义

特殊字符转义

字符转义
<&lt;
>&gt;
&&amp;
"&quot;
'&apos;

XML vs JSON

特性XMLJSON
大小较大较小
可读性
元数据支持属性不支持
命名空间支持不支持
数组无原生支持原生支持
解析复杂度较高较低

核心类型

1. Name - XML 名称

type Name struct {
    Space, Local string
}

功能:表示 XML 元素或属性的名称。

字段

  • Space:命名空间(可选)
  • Local:本地名称

示例

name := xml.Name{
    Space: "http://example.com/ns",
    Local: "person",
}

2. Attr - XML 属性

type Attr struct {
    Name  Name
    Value string
}

功能:表示 XML 元素的属性。

示例

<person id="123" active="true">

对应:

[]xml.Attr{
    {Name: xml.Name{Local: "id"}, Value: "123"},
    {Name: xml.Name{Local: "active"}, Value: "true"},
}

3. StartElement - 开始标签

type StartElement struct {
    Name Name
    Attr []Attr
}

功能:表示 XML 开始标签(如 <person>)。

示例

<person id="1">

对应:

xml.StartElement{
    Name: xml.Name{Local: "person"},
    Attr: []xml.Attr{
        {Name: xml.Name{Local: "id"}, Value: "1"},
    },
}

4. EndElement - 结束标签

type EndElement struct {
    Name Name
}

功能:表示 XML 结束标签(如 </person>)。


5. CharData - 字符数据

type CharData []byte

功能:表示 XML 元素之间的文本内容。

示例

<name>John Doe</name>

John Doe 就是 CharData。


6. Comment - 注释

type Comment []byte

功能:表示 XML 注释。

示例

<!-- 这是一个注释 -->

7. ProcInst - 处理指令

type ProcInst struct {
    Target string
    Inst   []byte
}

功能:表示 XML 处理指令。

示例

<?xml version="1.0" encoding="UTF-8"?>

8. Directive - XML 指令

type Directive []byte

功能:表示 XML DOCTYPE 声明。

示例

<!DOCTYPE html>

9. Token - XML Token 接口

type Token interface{}

功能:表示 XML Token,可以是 StartElement、EndElement、CharData 等。


10. Decoder - XML 解码器

type Decoder struct {
    // 包含过滤或未导出的字段
}

功能:流式解析 XML。

创建方法

func NewDecoder(r io.Reader) *Decoder

主要方法

// 读取下一个 Token
func (d *Decoder) Token() (t Token, err error)

// 解码到结构体
func (d *Decoder) Decode(v interface{}) error

// 解码到指定类型
func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error

11. Encoder - XML 编码器

type Encoder struct {
    // 包含过滤或未导出的字段
}

功能:流式编码 XML。

创建方法

func NewEncoder(w io.Writer) *Encoder

主要方法

// 编码 Token
func (e *Encoder) Encode(t Token) error

// 编码结构体
func (e *Encoder) Encode(v interface{}) error

// 编码元素
func (e *Encoder) EncodeElement(v interface{}, start StartElement) error

// 刷新缓冲区
func (e *Encoder) Flush() error

Struct Tag 详解

基本语法

type Struct struct {
    Field  Type  `xml:"name,flags"`
}

常用选项

选项说明示例
元素名自定义元素名xml:"person"
属性映射为属性xml:"id,attr"
字符数据映射为文本xml:",chardata"
注释映射为注释xml:",comment"
内联扁平化嵌套xml:",any"
通配符匹配任意元素xml:",any"
命名空间指定命名空间xml:"ns:person"
-忽略字段xml:"-"

元素名自定义

type Person struct {
    Name string `xml:"name"`      // 自定义元素名
    Age  int    `xml:"age"`
}

对应 XML:

<person>
    <name>John</name>
    <age>30</age>
</person>

属性映射

type Person struct {
    ID     int    `xml:"id,attr"`   // 属性
    Name   string `xml:"name"`      // 元素
}

对应 XML:

<person id="123">
    <name>John</name>
</person>

字符数据

type HTML struct {
    Content string `xml:",chardata"`
}

对应 XML:

<html>Some text content</html>

内联元素

type Company struct {
    Name    string `xml:"name"`
    People  []Person `xml:",any"`  // 内联任意元素
}

嵌套结构

type Address struct {
    City  string `xml:"city"`
    State string `xml:"state"`
}

type Person struct {
    Name    string  `xml:"name"`
    Address Address `xml:"address"`
}

对应 XML:

<person>
    <name>John</name>
    <address>
        <city>New York</city>
        <state>NY</state>
    </address>
</person>

切片和数组

type People struct {
    Persons []Person `xml:"person"`
}

对应 XML:

<people>
    <person>
        <name>John</name>
    </person>
    <person>
        <name>Jane</name>
    </person>
</people>

指针

type Document struct {
    Title string  `xml:"title"`
    Author *string `xml:"author"`  // 指针,可为 nil
}

核心函数

1. Marshal - 编码为 XML

func Marshal(v interface{}) ([]byte, error)

功能:将 Go 值编码为 XML 字节切片。

示例

type Person struct {
    Name string `xml:"name"`
    Age  int    `xml:"age"`
}

person := Person{Name: "John", Age: 30}
data, err := xml.Marshal(person)
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))
// 输出:<Person><name>John</name><age>30</age></Person>

2. MarshalIndent - 格式化编码

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

功能:将 Go 值编码为格式化的 XML(带缩进)。

示例

data, err := xml.MarshalIndent(person, "", "  ")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))
/*
输出:
<Person>
  <name>John</name>
  <age>30</age>
</Person>
*/

3. Unmarshal - 从 XML 解码

func Unmarshal(data []byte, v interface{}) error

功能:将 XML 数据解码到 Go 值。

示例

var person Person
err := xml.Unmarshal(data, &person)
if err != nil {
    log.Fatal(err)
}

4. NewDecoder - 创建解码器

func NewDecoder(r io.Reader) *Decoder

功能:创建流式 XML 解码器。


5. NewEncoder - 创建编码器

func NewEncoder(w io.Writer) *Encoder

功能:创建流式 XML 编码器。


完整示例

示例 1:基本编解码

package main

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

// Person 人员结构
type Person struct {
    XMLName xml.Name `xml:"person"`
    Name    string   `xml:"name"`
    Age     int      `xml:"age"`
    Email   string   `xml:"email"`
}

func main() {
    fmt.Println("=== XML 基本编解码 ===\n")
    
    // 1. 创建数据
    person := Person{
        Name:  "John Doe",
        Age:   30,
        Email: "john@example.com",
    }
    
    fmt.Printf("原始数据:\n")
    fmt.Printf("  Name: %s\n", person.Name)
    fmt.Printf("  Age: %d\n", person.Age)
    fmt.Printf("  Email: %s\n\n", person.Email)
    
    // 2. 编码为 XML
    fmt.Println("2. 编码为 XML:")
    data, err := xml.Marshal(person)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  紧凑格式:%s\n\n", string(data))
    
    // 格式化输出
    indentData, err := xml.MarshalIndent(person, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  格式化格式:\n%s\n\n", string(indentData))
    
    // 3. 从 XML 解码
    fmt.Println("3. 从 XML 解码:")
    var decoded Person
    err = xml.Unmarshal(data, &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  解码结果:\n")
    fmt.Printf("  Name: %s\n", decoded.Name)
    fmt.Printf("  Age: %d\n", decoded.Age)
    fmt.Printf("  Email: %s\n\n", decoded.Email)
    
    // 4. 验证
    fmt.Printf("验证:%v\n", person == decoded)
}

输出

=== XML 基本编解码 ===

原始数据:
  Name: John Doe
  Age: 30
  Email: john@example.com

2. 编码为 XML:
  紧凑格式:<Person><name>John Doe</name><age>30</age><email>john@example.com</email></Person>

  格式化格式:
<Person>
  <name>John Doe</name>
  <age>30</age>
  <email>john@example.com</email>
</Person>

3. 从 XML 解码:
  解码结果:
  Name: John Doe
  Age: 30
  Email: john@example.com

验证:true

示例 2:属性处理

package main

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

// Product 产品(包含属性)
type Product struct {
    XMLName   xml.Name `xml:"product"`
    ID        int      `xml:"id,attr"`
    Category  string   `xml:"category,attr"`
    Name      string   `xml:"name"`
    Price     float64  `xml:"price"`
    InStock   bool     `xml:"in_stock,attr"`
}

func main() {
    fmt.Println("=== XML 属性处理 ===\n")
    
    // 1. 创建数据
    product := Product{
        ID:       123,
        Category: "Electronics",
        Name:     "Laptop",
        Price:    999.99,
        InStock:  true,
    }
    
    // 2. 编码
    fmt.Println("2. 编码为 XML:")
    data, err := xml.MarshalIndent(product, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("%s\n\n", string(data))
    
    // 3. 解码
    fmt.Println("3. 从 XML 解码:")
    xmlStr := `<product id="456" category="Books" in_stock="true">
        <name>Go Programming</name>
        <price>49.99</price>
    </product>`
    
    var decoded Product
    err = xml.Unmarshal([]byte(xmlStr), &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  ID: %d\n", decoded.ID)
    fmt.Printf("  Category: %s\n", decoded.Category)
    fmt.Printf("  Name: %s\n", decoded.Name)
    fmt.Printf("  Price: %.2f\n", decoded.Price)
    fmt.Printf("  InStock: %v\n", decoded.InStock)
}

输出

=== XML 属性处理 ===

2. 编码为 XML:
  <Product id="123" category="Electronics" in_stock="true">
    <name>Laptop</name>
    <price>999.99</price>
  </Product>

3. 从 XML 解码:
  ID: 456
  Category: Books
  Name: Go Programming
  Price: 49.99
  InStock: true

示例 3:嵌套结构

package main

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

// Address 地址
type Address struct {
    Street  string `xml:"street"`
    City    string `xml:"city"`
    State   string `xml:"state"`
    ZipCode string `xml:"zip_code"`
    Country string `xml:"country"`
}

// Contact 联系方式
type Contact struct {
    Type  string `xml:"type,attr"`
    Value string `xml:",chardata"`
}

// Person 人员(嵌套结构)
type Person struct {
    XMLName   xml.Name  `xml:"person"`
    ID        int       `xml:"id,attr"`
    Name      string    `xml:"name"`
    Age       int       `xml:"age"`
    Address   Address   `xml:"address"`
    Contacts  []Contact `xml:"contact"`
}

func main() {
    fmt.Println("=== XML 嵌套结构 ===\n")
    
    // 1. 创建数据
    person := Person{
        ID:   1,
        Name: "John Doe",
        Age:  30,
        Address: Address{
            Street:  "123 Main St",
            City:    "New York",
            State:   "NY",
            ZipCode: "10001",
            Country: "USA",
        },
        Contacts: []Contact{
            {Type: "email", Value: "john@example.com"},
            {Type: "phone", Value: "+1-555-1234"},
        },
    }
    
    // 2. 编码
    fmt.Println("2. 编码为 XML:")
    data, err := xml.MarshalIndent(person, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("%s\n\n", string(data))
    
    // 3. 解码
    fmt.Println("3. 从 XML 解码:")
    var decoded Person
    err = xml.Unmarshal(data, &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  姓名:%s\n", decoded.Name)
    fmt.Printf("  年龄:%d\n", decoded.Age)
    fmt.Printf("  城市:%s\n", decoded.Address.City)
    fmt.Printf("  联系方式:%d 个\n", len(decoded.Contacts))
    
    for i, contact := range decoded.Contacts {
        fmt.Printf("    联系 %d: [%s] %s\n", i+1, contact.Type, contact.Value)
    }
}

输出

=== XML 嵌套结构 ===

2. 编码为 XML:
  <Person id="1">
    <name>John Doe</name>
    <age>30</age>
    <address>
      <street>123 Main St</street>
      <city>New York</city>
      <state>NY</state>
      <zip_code>10001</zip_code>
      <country>USA</country>
    </address>
    <contact type="email">john@example.com</contact>
    <contact type="phone">+1-555-1234</contact>
  </Person>

3. 从 XML 解码:
  姓名:John Doe
  年龄:30
  城市:New York
  联系方式:2 个
    联系 1: [email] john@example.com
    联系 2: [phone] +1-555-1234

示例 4:切片和数组

package main

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

// Item 项目
type Item struct {
    ID    int    `xml:"id,attr"`
    Name  string `xml:"name"`
    Value int    `xml:"value"`
}

// Inventory 库存
type Inventory struct {
    XMLName xml.Name `xml:"inventory"`
    Items   []Item   `xml:"item"`
}

func main() {
    fmt.Println("=== XML 切片和数组 ===\n")
    
    // 1. 创建数据
    inventory := Inventory{
        Items: []Item{
            {ID: 1, Name: "Laptop", Value: 10},
            {ID: 2, Name: "Mouse", Value: 50},
            {ID: 3, Name: "Keyboard", Value: 30},
        },
    }
    
    // 2. 编码
    fmt.Println("2. 编码为 XML:")
    data, err := xml.MarshalIndent(inventory, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("%s\n\n", string(data))
    
    // 3. 解码
    fmt.Println("3. 从 XML 解码:")
    xmlStr := `<inventory>
        <item id="4">
            <name>Monitor</name>
            <value>20</value>
        </item>
        <item id="5">
            <name>Webcam</name>
            <value>15</value>
        </item>
    </inventory>`
    
    var decoded Inventory
    err = xml.Unmarshal([]byte(xmlStr), &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  项目数:%d\n", len(decoded.Items))
    
    for _, item := range decoded.Items {
        fmt.Printf("    [%d] %s - 数量:%d\n", item.ID, item.Name, item.Value)
    }
}

输出

=== XML 切片和数组 ===

2. 编码为 XML:
  <Inventory>
    <Item id="1">
      <name>Laptop</name>
      <value>10</value>
    </Item>
    <Item id="2">
      <name>Mouse</name>
      <value>50</value>
    </Item>
    <Item id="3">
      <name>Keyboard</name>
      <value>30</value>
    </Item>
  </Inventory>

3. 从 XML 解码:
  项目数:2
    [4] Monitor - 数量:20
    [5] Webcam - 数量:15

示例 5:流式解析

package main

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

func main() {
    fmt.Println("=== XML 流式解析 ===\n")
    
    // XML 数据
    xmlStr := `<?xml version="1.0" encoding="UTF-8"?>
<catalog>
    <!-- 产品目录 -->
    <product id="1">
        <name>Laptop</name>
        <price>999.99</price>
    </product>
    <product id="2">
        <name>Mouse</name>
        <price>29.99</price>
    </product>
</catalog>`
    
    // 1. 创建解码器
    fmt.Println("1. 创建流式解码器:")
    decoder := xml.NewDecoder(strings.NewReader(xmlStr))
    
    // 2. 遍历所有 Token
    fmt.Println("2. 遍历 Token:")
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        
        switch t := token.(type) {
        case xml.ProcInst:
            fmt.Printf("  处理指令:%s %s\n", t.Target, string(t.Inst))
        case xml.Comment:
            fmt.Printf("  注释:%s\n", string(t))
        case xml.StartElement:
            fmt.Printf("  开始标签:%s", t.Name.Local)
            if len(t.Attr) > 0 {
                attrs := make([]string, len(t.Attr))
                for i, attr := range t.Attr {
                    attrs[i] = fmt.Sprintf("%s=%s", attr.Name.Local, attr.Value)
                }
                fmt.Printf(" (%s)", strings.Join(attrs, ", "))
            }
            fmt.Println()
        case xml.EndElement:
            fmt.Printf("  结束标签:%s\n", t.Name.Local)
        case xml.CharData:
            text := strings.TrimSpace(string(t))
            if text != "" {
                fmt.Printf("  文本:%s\n", text)
            }
        }
    }
    
    // 3. 使用 Decode 方法
    fmt.Println("\n3. 使用 Decode 方法:")
    
    type Product struct {
        ID    int     `xml:"id,attr"`
        Name  string  `xml:"name"`
        Price float64 `xml:"price"`
    }
    
    type Catalog struct {
        Products []Product `xml:"product"`
    }
    
    decoder = xml.NewDecoder(strings.NewReader(xmlStr))
    var catalog Catalog
    
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        
        if startElem, ok := token.(xml.StartElement); ok {
            if startElem.Name.Local == "product" {
                var product Product
                err := decoder.DecodeElement(&product, &startElem)
                if err != nil {
                    log.Fatal(err)
                }
                fmt.Printf("  产品:%d - %s ($%.2f)\n", 
                    product.ID, product.Name, product.Price)
            }
        }
    }
}

输出

=== XML 流式解析 ===

1. 创建流式解码器:
2. 遍历 Token:
  处理指令:xml version="1.0" encoding="UTF-8"
  开始标签:catalog
  注释: 产品目录 
  开始标签:product (id=1)
  开始标签:name
  文本:Laptop
  结束标签:name
  开始标签:price
  文本:999.99
  结束标签:price
  结束标签:product
  开始标签:product (id=2)
  开始标签:name
  文本:Mouse
  结束标签:name
  开始标签:price
  文本:29.99
  结束标签:price
  结束标签:product
  结束标签:catalog

3. 使用 Decode 方法:
  产品:1 - Laptop ($999.99)
  产品:2 - Mouse ($29.99)

示例 6:命名空间处理

package main

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

// Person 带命名空间
type Person struct {
    XMLName xml.Name `xml:"http://example.com/ns person"`
    Name    string   `xml:"http://example.com/ns name"`
    Age     int      `xml:"http://example.com/ns age"`
}

func main() {
    fmt.Println("=== XML 命名空间处理 ===\n")
    
    // 1. 编码
    fmt.Println("1. 编码带命名空间的 XML:")
    
    person := Person{
        Name: "John Doe",
        Age:  30,
    }
    
    data, err := xml.MarshalIndent(person, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("%s\n\n", string(data))
    
    // 2. 解码
    fmt.Println("2. 解码带命名空间的 XML:")
    
    xmlStr := `<?xml version="1.0" encoding="UTF-8"?>
<ns:person xmlns:ns="http://example.com/ns">
    <ns:name>Jane Smith</ns:name>
    <ns:age>25</ns:age>
</ns:person>`
    
    var decoded Person
    err = xml.Unmarshal([]byte(xmlStr), &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  Name: %s\n", decoded.Name)
    fmt.Printf("  Age: %d\n", decoded.Age)
}

输出

=== XML 命名空间处理 ===

1. 编码带命名空间的 XML:
  <person xmlns="http://example.com/ns">
    <name>John Doe</name>
    <age>30</age>
  </person>

2. 解码带命名空间的 XML:
  Name: Jane Smith
  Age: 25

示例 7:自定义编解码

package main

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

// CustomInt 自定义整数类型
type CustomInt int

// MarshalXML 自定义编码
func (c CustomInt) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    // 编码为字符串
    return e.EncodeElement(strconv.Itoa(int(c)), start)
}

// UnmarshalXML 自定义解码
func (c *CustomInt) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var s string
    err := d.DecodeElement(&s, &start)
    if err != nil {
        return err
    }
    
    // 从字符串解析
    val, err := strconv.Atoi(strings.TrimSpace(s))
    if err != nil {
        return err
    }
    
    *c = CustomInt(val)
    return nil
}

// Data 包含自定义类型
type Data struct {
    XMLName xml.Name  `xml:"data"`
    ID      CustomInt `xml:"id"`
    Name    string    `xml:"name"`
    Value   int       `xml:"value"`
}

func main() {
    fmt.Println("=== XML 自定义编解码 ===\n")
    
    // 1. 创建数据
    data := Data{
        ID:    123,
        Name:  "Test",
        Value: 456,
    }
    
    // 2. 编码
    fmt.Println("2. 编码:")
    xmlData, err := xml.MarshalIndent(data, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("%s\n\n", string(xmlData))
    
    // 3. 解码
    fmt.Println("3. 解码:")
    xmlInput := `<data>
        <id>789</id>
        <name>Custom</name>
        <value>999</value>
    </data>`
    
    var decoded Data
    err = xml.Unmarshal([]byte(xmlInput), &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("  ID: %d (类型:%T)\n", decoded.ID, decoded.ID)
    fmt.Printf("  Name: %s\n", decoded.Name)
    fmt.Printf("  Value: %d\n", decoded.Value)
}

输出

=== XML 自定义编解码 ===

2. 编码:
  <Data>
    <id>123</id>
    <name>Test</name>
    <value>456</value>
  </Data>

3. 解码:
  ID: 789 (类型:main.CustomInt)
  Name: Custom
  Value: 999

示例 8:错误处理

package main

import (
    "encoding/xml"
    "fmt"
)

// Person 人员
type Person struct {
    Name string `xml:"name"`
    Age  int    `xml:"age"`
}

func main() {
    fmt.Println("=== XML 错误处理 ===\n")
    
    // 1. 无效 XML 语法
    fmt.Println("1. 无效 XML 语法:")
    
    invalidXMLs := []struct {
        xml  string
        desc string
    }{
        {`<person><name>John</name>`, "缺少结束标签"},
        {`<person><name>John</name></person`, "缺少 >"},
        {`<person><name>John</name></PERSON>`, "标签大小写不匹配"},
        {`<person name=John>`, "属性值未加引号"},
        {`<person><name>John & Jane</name></person>`, "未转义的 &"},
    }
    
    for i, tc := range invalidXMLs {
        var person Person
        err := xml.Unmarshal([]byte(tc.xml), &person)
        fmt.Printf("  测试 %d (%s):\n", i+1, tc.desc)
        if err != nil {
            fmt.Printf("    ✗ 错误:%v\n\n", err)
        } else {
            fmt.Printf("    ? 意外成功\n\n")
        }
    }
    
    // 2. 类型不匹配
    fmt.Println("2. 类型不匹配:")
    
    typeMismatch := `<person><name>John</name><age>not_a_number</age></person>`
    var person Person
    err := xml.Unmarshal([]byte(typeMismatch), &person)
    fmt.Printf("  XML: %s\n", typeMismatch)
    if err != nil {
        fmt.Printf("  ✗ 错误:%v\n\n", err)
    }
    
    // 3. 字段缺失和多余
    fmt.Println("3. 字段缺失和多余:")
    
    // 字段缺失
    missingField := `<person><name>John</name></person>`
    var person2 Person
    err = xml.Unmarshal([]byte(missingField), &person2)
    fmt.Printf("  字段缺失:\n")
    fmt.Printf("    XML: %s\n", missingField)
    if err != nil {
        fmt.Printf("    ✗ 错误:%v\n", err)
    } else {
        fmt.Printf("    ✓ 成功(缺失字段为零值)\n")
        fmt.Printf("    结果:%+v\n\n", person2)
    }
    
    // 字段多余
    extraField := `<person><name>John</name><age>30</age><email>john@example.com</email></person>`
    var person3 Person
    err = xml.Unmarshal([]byte(extraField), &person3)
    fmt.Printf("  字段多余:\n")
    fmt.Printf("    XML: %s\n", extraField)
    if err != nil {
        fmt.Printf("    ✗ 错误:%v\n", err)
    } else {
        fmt.Printf("    ✓ 成功(多余字段被忽略)\n")
        fmt.Printf("    结果:%+v\n\n", person3)
    }
    
    // 4. 空 XML
    fmt.Println("4. 空 XML:")
    
    emptyXML := ``
    var person4 Person
    err = xml.Unmarshal([]byte(emptyXML), &person4)
    fmt.Printf("  空数据:\n")
    if err != nil {
        fmt.Printf("    ✗ 错误:%v\n", err)
    }
}

输出

=== XML 错误处理 ===

1. 无效 XML 语法:
  测试 1 (缺少结束标签):
    ✗ 错误:XML syntax error on line 1: unexpected EOF

  测试 2 (缺少 >):
    ✗ 错误:XML syntax error on line 1: expected '>' in tag

  测试 3 (标签大小写不匹配):
    ✗ 错误:XML syntax error on line 1: element <name> closed by </PERSON>

  测试 4 (属性值未加引号):
    ✗ 错误:XML syntax error on line 1: expected "=" after attribute name

  测试 5 (未转义的 &):
    ✗ 错误:XML syntax error on line 1: invalid character entity & (no semicolon)

2. 类型不匹配:
  XML: <person><name>John</name><age>not_a_number</age></person>
  ✗ 错误:strconv.ParseInt: parsing "not_a_number": invalid syntax

3. 字段缺失和多余:
  字段缺失:
    XML: <person><name>John</name></person>
    ✓ 成功(缺失字段为零值)
    结果:{Name:John Age:0}
  字段多余:
    XML: <person><name>John</name><age>30</age><email>john@example.com</email></person>
    ✓ 成功(多余字段被忽略)
    结果:{Name:John Age:30}

4. 空 XML:
  空数据:

最佳实践

✅ 推荐做法

  1. 总是检查错误

    // ✅ 推荐
    data, err := xml.Marshal(v)
    if err != nil {
        return err
    }
    
    err = xml.Unmarshal(data, &v)
    if err != nil {
        return err
    }
    
  2. 使用 struct tag 自定义元素名

    // ✅ 推荐
    type Person struct {
        Name string `xml:"name"`
        Age  int    `xml:"age"`
    }
    
    // ❌ 不推荐
    type Person struct {
        Name string  // 使用默认字段名
        Age  int
    }
    
  3. 流式处理大文件

    // ✅ 推荐:大文件
    decoder := xml.NewDecoder(file)
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        // 处理 token
    }
    
  4. 使用指针处理可选元素

    // ✅ 推荐
    type Document struct {
        Title string   `xml:"title"`
        Author *string  `xml:"author"`  // 可为 nil
    }
    
  5. 处理命名空间

    // ✅ 推荐:明确命名空间
    type Person struct {
        XMLName xml.Name `xml:"http://example.com/ns person"`
        Name    string   `xml:"http://example.com/ns name"`
    }
    

❌ 不安全做法

  1. 不要忽略错误

    // ❌ 错误
    xml.Unmarshal(data, &v)
    
    // ✅ 正确
    if err := xml.Unmarshal(data, &v); err != nil {
        return err
    }
    
  2. 不要信任输入数据

    // ❌ 错误
    var v MyStruct
    xml.Unmarshal(input, &v)  // 未验证
    
    // ✅ 正确
    if err := xml.Unmarshal(input, &v); err != nil {
        return err
    }
    // 验证 v 的字段
    
  3. 不要混用格式

    // ❌ 错误
    // 在同一文档中混用不同风格
    
    // ✅ 正确
    // 保持一致的命名和结构
    

性能优化

1. 使用 MarshalIndent 代替手动格式化

// ✅ 推荐
data, _ := xml.MarshalIndent(v, "", "  ")

2. 预分配切片

// ✅ 推荐
items := make([]Item, 0, expectedCount)
xml.Unmarshal(data, &items)

3. 重用 Encoder/Decoder

// ✅ 推荐:重用
encoder := xml.NewEncoder(buf)
for _, item := range items {
    encoder.Encode(item)
}

总结

核心类型

类型用途说明
NameXML 名称Space + Local
Attr属性Name + Value
StartElement开始标签Name + Attr
EndElement结束标签Name
CharData文本内容[]byte
Decoder解码器流式解析
Encoder编码器流式编码

核心函数

函数用途返回值
Marshal编码为 XML[]byte, error
MarshalIndent格式化编码[]byte, error
Unmarshal从 XML 解码error
NewDecoder创建解码器*Decoder
NewEncoder创建编码器*Encoder

Struct Tag 选项

选项说明示例
元素名自定义元素名xml:"name"
attr映射为属性xml:"id,attr"
chardata映射为文本xml:",chardata"
comment映射为注释xml:",comment"
any通配符xml:",any"
-忽略字段xml:"-"

常见错误

错误原因解决方法
XML syntax error语法错误检查标签闭合
invalid character entity未转义字符使用 < >
expected “=”属性格式错误属性值加引号

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+