Golang如何解析含同名嵌套节点的XML并提取所有claim-text内容?
问题原因
你原有结构体的标签规则只能匹配<claim>节点的直接子层级<claim-text>标签,Go标准库encoding/xml默认不会递归查找深层嵌套的同名标签,因此无法获取嵌套的文本内容。
实现方案
我们可以为Claim类型实现自定义的UnmarshalXML方法,在反序列化阶段递归遍历当前<claim>节点下的所有子节点,提取所有层级<claim-text>标签的文本内容,完整实现代码如下:
package main import ( "encoding/xml" "fmt" "io" "strings" ) // 顶层解析结果结构体 type Result struct { Claims []Claim `xml:"claims>claim"` } // 自定义Claim类型,存储当前claim下所有claim-text的文本 type Claim struct { AllClaimTexts []string } // 实现encoding/xml的Unmarshaler接口,自定义反序列化逻辑 func (c *Claim) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // 递归提取claim-text文本的内部函数 var extractClaimText func(xml.Token) error extractClaimText = func(t xml.Token) error { switch token := t.(type) { case xml.StartElement: // 匹配所有层级的claim-text标签 if token.Name.Local == "claim-text" { var text string // 直接解析当前claim-text节点的全部文本(自动拼接内部嵌套的所有文本内容) if err := d.DecodeElement(&text, &token); err != nil { return err } // 按需清理文本前后空白,不需要可以删除这行 text = strings.TrimSpace(text) if text != "" { c.AllClaimTexts = append(c.AllClaimTexts, text) } return nil } // 非claim-text标签,继续遍历子节点 for { nextToken, err := d.Token() if err != nil { if err == io.EOF { break } return err } // 遇到当前节点的结束标签,停止子节点遍历 if _, ok := nextToken.(xml.EndElement); ok { break } if err := extractClaimText(nextToken); err != nil { return err } } } return nil } // 遍历当前claim节点的所有子节点 for { t, err := d.Token() if err != nil { if err == io.EOF { break } return err } // 遇到claim的结束标签,终止解析 if end, ok := t.(xml.EndElement); ok && end.Name.Local == "claim" { break } if err := extractClaimText(t); err != nil { return err } } return nil } // 测试用例 func main() { xmlData := ` <claims> <claim> <claim-text>ABC <claim-text>PQR</claim-text> <claim-text>Xyz <claim-text>A</claim-text> <claim-text>B</claim-text> <claim-text>C</claim-text> </claim-text> </claim-text> </claim> <claim> <claim-text>PPP <claim-text>ZZZ</claim-text> <claim-text>MMM</claim-text> </claim-text> </claim> </claims>` var res Result err := xml.Unmarshal([]byte(xmlData), &res) if err != nil { panic(err) } // 打印结果 for idx, claim := range res.Claims { fmt.Printf("第%d个claim的所有claim-text内容:\n", idx+1) for _, text := range claim.AllClaimTexts { fmt.Printf("- %s\n", text) } } }
输出效果
运行上述代码会得到你需要的结果:
第1个claim的所有claim-text内容: - ABC - PQR - Xyz - A - B - C 第2个claim的所有claim-text内容: - PPP - ZZZ - MMM
如果不需要清理文本前后的空白、换行,删除strings.TrimSpace相关逻辑即可。
内容的提问来源于stack exchange,提问作者Priyanka
相关产品推荐
相关产品推荐

