
本文介绍如何使用 Go 的 encoding/xml 包解析结构不规则的 XML(如多个同名标签含不同属性),并筛选出 type="Genres" 的 节点,安全高效地映射为 []Genre 结构。
本文介绍如何使用 go 的 `encoding/xml` 包解析结构不规则的 xml(如多个同名标签含不同属性),并筛选出 `type="genres"` 的 `
在 Go 中处理类似
✅ 推荐结构设计
首先定义可准确反映 XML 层级的 Go 结构体:
type Manga struct {
WorkID int `xml:"id,attr"`
Name string `xml:"name,attr"`
Precision string `xml:"precision,attr"`
Info []Info `xml:"info"`
}
type Info struct {
Type string `xml:"type,attr"`
Value string `xml:",chardata"`
}
注意:xml:",chardata" 表示该字段映射 XML 文本内容(即
✅ 解析与提取 Genres 的完整流程
package main
import (
"encoding/xml"
"fmt"
)
type Genre struct {
Value string
}
func main() {
xmlData := `<manga id="4199" type="manga" name="Jinki: Extend" precision="manga"><info type="Main title" lang="EN">Jinki: Extend</info><info type="Genres">action</info><info type="Genres">science fiction</info><info type="Themes">mecha</info><info type="Number of tankoubon">9</info><info type="Number of pages">186</info></manga>`
var manga Manga
err := xml.Unmarshal([]byte(xmlData), &manga)
if err != nil {
panic(err)
}
// 提取所有 type="Genres" 的 info 节点,并构造 []Genre
var genres []Genre
for _, info := range manga.Info {
if info.Type == "Genres" {
genres = append(genres, Genre{Value: info.Value})
}
}
fmt.Printf("Found %d genres: %+v\n", len(genres), genres)
// 输出:Found 2 genres: [{Value:action} {Value:science fiction}]
}
⚠️ 注意事项与最佳实践
- 不要尝试用嵌套结构硬编码 Genres 字段:Go 的 xml 包不支持基于属性值的条件解码(如“仅当 type="Genres" 时才填入某字段”),强行设计 Genres []Genre \xml:"info[type=Genres]"` 是无效的(XML tag 不支持属性选择器语法)。
- 空值与边界安全:实际使用中建议对 info.Value 做 strings.TrimSpace() 处理,避免前后空白符;同时可添加 if info.Type != "" && info.Value != "" 进一步校验。
- 扩展性考虑:若后续还需提取 Themes 或 Number of tankoubon,只需复用同一 Info 列表,无需修改结构定义——真正实现“一次解码,多路提取”。
通过这种“先泛化解码、后结构化筛选”的模式,你既能保持代码健壮性与可维护性,又能灵活应对 XML 中属性驱动的异构数据,是处理非标准 XML 的 Go 实践范式。











