Golang encoding/xml生成重复标签XML的问题问询
问题解答
1. 结构体方式解决重复标签冲突的方法
你的写法确实有问题——encoding/xml不允许同一个结构体里多个非切片字段使用相同的xml标签,这就是报错的原因。要解决重复<key>标签的问题,核心是把键值对作为一组单元处理,用切片存储这些单元,既能保证顺序,又能生成重复的<key>标签。
示例代码:
import ( "fmt" "encoding/xml" "os" ) // 封装dict中的键值对单元 type DictEntry struct { Key string `xml:"key"` Value interface{} `xml:",omitempty"` } // 自定义类型,用来指定不同的内容标签 type Data string `xml:"data"` type Integer int `xml:"integer"` type Real float64 `xml:"real"` type XMLStringArray struct { XMLName xml.Name `xml:"array"` Strings []string `xml:"string"` } type XMLDict struct { XMLName xml.Name `xml:"dict"` Entries []DictEntry `xml:",any"` // ",any"表示按顺序序列化切片内所有元素 } func main() { // 构造Points数组 points := []string{ "{0, 0}", "{0.05, 0.053871}", "{0.1, 0.110555}", // 其他点省略 } array := XMLStringArray{Strings: points} // 构造Curve子dict curveDict := XMLDict{ Entries: []DictEntry{ {Key: "Interpolation", Value: Integer(5)}, {Key: "Points", Value: array}, }, } // 构造主dict master := XMLDict{ Entries: []DictEntry{ {Key: "Color", Value: Data("BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU0NvbG9yAISECE5TT2Jq\n ZWN0AIWEAWMChARmZmZmg7oehT2DZmYmP4NmZiY/AYY=")}, {Key: "Curve", Value: curveDict}, {Key: "Line Width", Value: Real(0.0040000001899898052)}, {Key: "Points Diameter", Value: Real(0.014999999664723873)}, {Key: "Precision", Value: Real(9.9999997473787516e-05)}, }, } output, err := xml.MarshalIndent(master, " ", " ") if err != nil { fmt.Printf("error: %v\n", err) return } os.Stdout.Write(output) }
关键逻辑:
- 用
DictEntry封装每个<key>和对应内容 XMLDict通过[]DictEntry存储所有键值对,",any"标签确保按顺序序列化- 自定义类型(
Data/Integer等)用来生成不同的内容标签(如<data>/<integer>)
2. 更优的XML生成方式
如果XML格式固定且复杂,有两种比纯结构体更灵活的方案:
方案一:用xml.TokenWriter手动生成XML
完全掌控每个标签的顺序和内容,适合对格式要求极高的场景:
import ( "encoding/xml" "os" "fmt" ) func main() { writer := xml.NewEncoder(os.Stdout) writer.Indent(" ", " ") // 开始主dict if err := writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "dict"}}); err != nil { fmt.Printf("error: %v\n", err) return } // 写入Color键值对 writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "key"}}) writer.EncodeToken(xml.CharData("Color")) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "key"}}) writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "data"}}) writer.EncodeToken(xml.CharData("BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU0NvbG9yAISECE5TT2Jq\n ZWN0AIWEAWMChARmZmZmg7oehT2DZmYmP4NmZiY/AYY=")) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "data"}}) // 写入Curve键值对(含子dict和array) writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "key"}}) writer.EncodeToken(xml.CharData("Curve")) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "key"}}) // 子dict writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "dict"}}) // Interpolation writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "key"}}) writer.EncodeToken(xml.CharData("Interpolation")) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "key"}}) writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "integer"}}) writer.EncodeToken(xml.CharData("5")) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "integer"}}) // Points数组 writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "key"}}) writer.EncodeToken(xml.CharData("Points")) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "key"}}) writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "array"}}) points := []string{"{0,0}", "{0.05,0.053871}"} for _, p := range points { writer.EncodeToken(xml.StartElement{Name: xml.Name{Local: "string"}}) writer.EncodeToken(xml.CharData(p)) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "string"}}) } writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "array"}}) writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "dict"}}) // 其他键值对省略... // 结束主dict if err := writer.EncodeToken(xml.EndElement{Name: xml.Name{Local: "dict"}}); err != nil { fmt.Printf("error: %v\n", err) return } if err := writer.Flush(); err != nil { fmt.Printf("error: %v\n", err) } }
方案二:用text/template生成XML字符串
适合结构固定、可读性要求高的场景,先写模板再填充数据:
模板文件xml_template.tmpl:
<dict> <key>Color</key> <data>{{.ColorData}}</data> <key>Curve</key> <dict> <key>Interpolation</key> <integer>{{.Interpolation}}</integer> <key>Points</key> <array> {{range .Points}} <string>{{.}}</string> {{end}} </array> </dict> <key>Line Width</key> <real>{{.LineWidth}}</real> <!-- 其他字段 --> </dict>
Go代码:
import ( "os" "text/template" ) type TemplateData struct { ColorData string Interpolation int Points []string LineWidth float64 // 其他字段 } func main() { data := TemplateData{ ColorData: "BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU0NvbG9yAISECE5TT2Jq\n ZWN0AIWEAWMChARmZmZmg7oehT2DZmYmP4NmZiY/AYY=", Interpolation: 5, Points: []string{"{0,0}", "{0.05,0.053871}"}, LineWidth: 0.0040000001899898052, } tmpl, err := template.ParseFiles("xml_template.tmpl") if err != nil { panic(err) } err = tmpl.Execute(os.Stdout, data) if err != nil { panic(err) } }
3. 你的写法错误在哪里
是的,你的写法存在明显错误:
- 在
XMLDict结构体中,Key1和Key2都使用了xml:"key"标签,而encoding/xml要求同一个结构体中,非切片类型的字段不能重复使用相同标签,因此触发冲突报错。 - 这种写法扩展性极差,新增键值对需要添加新字段,且无法灵活保证键与对应内容的顺序关联。
正确思路应该是把键值对作为一个整体单元,用切片存储多个单元,这样既能生成重复的<key>标签,又能维护键值的对应关系和顺序。
内容的提问来源于stack exchange,提问作者Jadefox10200
相关产品推荐
相关产品推荐

