I seem to be having a problem getting the marshaller to call MarshalXML for the root node. I have a struct for storing the XML like so:
type XmlNode struct {
Name string `xml:"-"`
Attrs map[string]string `xml:"-"`
Children []XmlNode `xml:",any"`
}
I have a MarshalXML function like so:
func (n *XmlNode) MarshalXML(encoder *xml.Encoder, start xml.StartElement) error {
fmt.Println("Inside MarshalXML")
// Name
start.Name = xml.Name{Local: n.Name}
// Attrs
for k, v := range n.Attrs {
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: k}, Value: v})
}
type node XmlNode
return encoder.EncodeElement((*node)(n), start)
}
And I'm calling it with code similar to:
func main() {
root := XmlNode{Name: "root", Attrs: map[string]string{}}
root.Attrs["rootAttr"] = "rootValue"
child := XmlNode{Name: "child", Attrs: map[string]string{}}
child.Attrs["childAttr"] = "childValue"
root.Children = append(root.Children, child)
out, _ := xml.Marshal(root)
fmt.Println(string(out))
}
When it runs, I see the following output:
Inside MarshalXML
<XmlNode><child childAttr="childValue"></child></XmlNode>
I'm expecting:
Inside MarshalXML
Inside MarshalXML
<root rootAttr="rootValue"><child childAttr="childValue"></child></root>
I know that I can code the name into a specific attribute, but I'm more concerned with handling the attributes from a map. I'm also likely not supposed to be modifying the start element there, but it seems to work for this. My problem is that the MarshalXML doesn't get called for the root element, but it does for any children. So, even if I were to do something correctly there, I'm not sure I can intercept that root element to modify it appropriately.
I briefly tried using xml.Encode directly, but it seems to follow into the same path to build the start template and root, and then calls the MarshalXML for the Children node. Is there a clean way to get XmlNode converted into xml.StartElement so I can pass that in? I can write a function on XmlNode directly that'll start the marshalling by making a StartElement and calling EncodeElement or Encode, but I'm trying to avoid that and stick with the xml.Marshal call, to prevent some confusion for other devs.
Any thoughts?
Here's a Go Playground link to the above code in a runnable form.