Go XML 处理入门:用 encoding/xml 解析配置和旧系统数据

本文讲解 Go encoding/xml 的结构体标签、属性解析、嵌套元素、编码输出和流式解码,适合需要对接 XML 数据的初学者。

XML 还没有从工程世界消失

现在很多新接口都用 JSON,但 XML 依然存在于不少地方:老系统接口、支付或银行协议、配置文件、RSS、Sitemap、办公文档格式。Go 标准库提供了 encoding/xml,可以像处理 JSON 一样把 XML 映射到结构体,也可以用流式方式逐个 token 解析。

XML 比 JSON 更啰嗦,也多了属性、命名空间、文本节点、嵌套元素等概念。入门阶段不必一口气掌握所有细节,只要能读懂常见结构、正确写标签、处理错误,就能应付很多对接任务。

这篇文章用一个服务配置和一个订单 XML 做例子,讲解 encoding/xml 的基础用法。

解析简单 XML

XML:

<config>
  <name>demo-service</name>
  <port>8080</port>
</config>

结构体:

type Config struct {
	XMLName xml.Name `xml:"config"`
	Name    string   `xml:"name"`
	Port    int      `xml:"port"`
}

解析:

var cfg Config
if err := xml.Unmarshal(data, &cfg); err != nil {
	return err
}
fmt.Println(cfg.Name, cfg.Port)

XMLName 可以帮助确认根元素名称。它不是必须字段,但在配置解析里很有用。

从文件读取:

func LoadConfig(path string) (Config, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return Config{}, fmt.Errorf("read config: %w", err)
	}

	var cfg Config
	if err := xml.Unmarshal(data, &cfg); err != nil {
		return Config{}, fmt.Errorf("parse config: %w", err)
	}
	return cfg, nil
}

解析成功后仍然要做业务校验:

if cfg.Port <= 0 {
	return Config{}, fmt.Errorf("port must be positive")
}

解析属性

XML 属性:

<service name="user-api" enabled="true">
  <port>8080</port>
</service>

结构体:

type Service struct {
	Name    string `xml:"name,attr"`
	Enabled bool   `xml:"enabled,attr"`
	Port    int    `xml:"port"`
}

,attr 表示字段来自属性,而不是子元素。

解析:

var service Service
if err := xml.Unmarshal(data, &service); err != nil {
	return err
}

属性适合短小元信息,复杂内容通常放子元素。对接外部 XML 时,结构由对方决定;自己设计配置时,尽量保持简单。

嵌套元素和切片

订单 XML:

<order id="1001">
  <user>xiaolin</user>
  <items>
    <item sku="book-go">
      <name>Go 入门书</name>
      <quantity>1</quantity>
    </item>
    <item sku="sticker">
      <name>贴纸</name>
      <quantity>3</quantity>
    </item>
  </items>
</order>

结构体:

type Order struct {
	ID    int64       `xml:"id,attr"`
	User  string      `xml:"user"`
	Items []OrderItem `xml:"items>item"`
}

type OrderItem struct {
	SKU      string `xml:"sku,attr"`
	Name     string `xml:"name"`
	Quantity int    `xml:"quantity"`
}

xml:"items>item" 表示进入 items 子元素,再读取多个 item

使用:

var order Order
if err := xml.Unmarshal(data, &order); err != nil {
	return err
}
for _, item := range order.Items {
	fmt.Println(item.SKU, item.Quantity)
}

XML 标签要和结构严格对应。字段解析不出来时,先检查路径、属性标记和大小写。

输出 XML

编码:

order := Order{
	ID:   1001,
	User: "xiaolin",
	Items: []OrderItem{
		{SKU: "book-go", Name: "Go 入门书", Quantity: 1},
	},
}

data, err := xml.MarshalIndent(order, "", "  ")
if err != nil {
	return err
}

fmt.Println(xml.Header + string(data))

xml.Header 是:

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

如果要写到响应或文件,可以用 Encoder:

encoder := xml.NewEncoder(w)
encoder.Indent("", "  ")
if err := encoder.Encode(order); err != nil {
	return err
}

小结

Go 的 encoding/xml 可以通过结构体标签处理 XML 元素、属性和嵌套路径。简单 XML 用 xml.Unmarshal 很方便,大文件或复杂流可以用 xml.Decoder。解析后不要忘记业务校验,尤其是配置文件和外部接口。

XML 虽然不如 JSON 常见,但仍然是工程世界的一部分。掌握标准库基本用法,在遇到老系统、RSS、Sitemap 或企业接口时,就不会手忙脚乱。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页