Go语言解析动态XML标签问题:Unmarshal后Images字段为空求助
解决Go XML Unmarshal动态标签的问题
你的问题出在直接用map[string]string映射<images>标签的方式不对——Go的encoding/xml包并不会自动把<images>下的子元素解析成map,所以你的Images字段才会是空的。另外还有个小细节:你的Products结构体里Product字段是单个结构体,但XML中<products>下可能包含多个<product>,定义为切片[]Product会更合理。
下面给你两种可行的解决方案:
方案一:自定义结构体并实现xml.Unmarshaler接口
这种方法最灵活,还能兼容CDATA等特殊内容格式:
首先修改结构体定义:
import ( "encoding/xml" "io" ) type Products struct { XMLName xml.Name `xml:"products"` Products []Product `xml:"product"` // 改成切片支持多个产品 } type Product struct { ProductID string `xml:"product_id"` DateCreated string `xml:"date_created"` Price string `xml:"price"` StockStatus string `xml:"stock_status"` Images Images `xml:"images"` } type Images struct { Items map[string]string // 用来存储动态image标签的键值对 }
然后给Images结构体实现UnmarshalXML接口,手动解析<images>下的所有子元素:
func (i *Images) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { i.Items = make(map[string]string) // 遍历<images>下的所有节点 for { var node xml.Node err := d.Decode(&node) if err != nil { if err == io.EOF { break // 解析到结束标签,退出循环 } return err } // 只处理元素节点(比如<image_1>、<image_2>这类标签) if node.Type == xml.ElementNode { var content string // 兼容普通文本和CDATA两种内容格式 for child := node.FirstChild; child != nil; child = child.NextSibling { if child.Type == xml.CharData || child.Type == xml.CDATA { content += string(child.Data) } } i.Items[node.Data] = content } } return nil }
测试代码
func main() { xmlData := `<?xml version="1.0" encoding="utf-8"?> <products> <product> <product_id>11600</product_id> <date_created><![CDATA[2018-10-19 15:20:22]]></date_created> <price>200</price> <stock_status>In Stock</stock_status> <images> <image_1>1.jpg</image_1> <image_2>2.jpg</image_2> </images> </product> </products>` var products Products err := xml.Unmarshal([]byte(xmlData), &products) if err != nil { panic(err) } if len(products.Products) > 0 { p := products.Products[0] fmt.Println("Images count:", len(p.Images.Items)) // 输出2 for key, val := range p.Images.Items { fmt.Printf("%s: %s\n", key, val) } } }
方案二:使用xml.MapElement简化处理
如果你的动态标签都是简单的键值对(没有嵌套结构),可以直接用xml.MapElement快速解析:
修改Product结构体的Images字段:
type Product struct { ProductID string `xml:"product_id"` DateCreated string `xml:"date_created"` Price string `xml:"price"` StockStatus string `xml:"stock_status"` Images xml.MapElement `xml:"images"` // 使用xml.MapElement }
解析后转成map使用:
func main() { // 省略XML数据和Unmarshal代码... if len(products.Products) > 0 { p := products.Products[0] imagesMap := make(map[string]string) for _, elem := range p.Images { imagesMap[elem.Key] = elem.Value } fmt.Println("Images count:", len(imagesMap)) // 输出2 } }
为什么你原来的写法不行?
当你给Images字段加xml:"images"标签时,encoding/xml包会尝试把<images>标签的内容直接解析成map[string]string,但map并没有内置的XML解析规则——XML包不知道要把<images>下的子元素当作map的键值对,所以解析失败,最终得到一个空map。
内容的提问来源于stack exchange,提问作者MZON
相关产品推荐
相关产品推荐

