'Go XML Marshalling and the Root Element
In Go, you can marshall a struct to XML, e.g.:
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
Name string
Starsign string
}
func main() {
p := &person{"John Smith", "Capricorn"}
b,_ := xml.MarshalIndent(p,""," ")
fmt.Println(string(b))
}
produces output:
<person>
<Name>John Smith</Name>
<Starsign>Capricorn</Starsign>
</person>
My problem is, the person type is lower-case "p" because I want that to be private to the package. But I'd prefer the XML element to be uppercase: <Person>
. The fields within the struct can be marshalled to other names using tags (e.g. `xml:"name"`) against the structure fields but this doesn't seem to be an option for the structure type.
I have a work-around using templates, but it would be nice to know a better answer.
Solution 1:[1]
According to the encoding/xml.Marshal documentation:
The name for the XML elements is taken from, in order of preference:
- the tag on the XMLName field, if the data is a struct
- the value of the XMLName field of type xml.Name
- the tag of the struct field used to obtain the data
- the name of the struct field used to obtain the data
- the name of the marshalled type
You can use a tag on the XMLName field in the struct to override the person struct's XML tag name. In order to avoid putting it in your actual person struct, you can create an anonymous struct that embeds the person struct you are marshaling.
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
Name string
Starsign string
}
func marshalPerson(p person) ([]byte, error) {
tmp := struct {
person
XMLName struct{} `xml:"Person"`
}{person: p}
return xml.MarshalIndent(tmp, "", " ")
}
func main() {
p := person{"John Smith", "Capricorn"}
b, _ := marshalPerson(p)
fmt.Println(string(b))
}
Solution 2:[2]
This also works, though I don't think it's particularly pretty.
However, this worked in a lot more straight forward manner for me than the other accepted solution from 5 years ago.
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
XMLName xml.Name
Name string
Starsign string
}
func main() {
p := &person{xml.Name{Local: "Person"}, "John Smith", "Capricorn"}
b,_ := xml.MarshalIndent(p,""," ")
fmt.Println(string(b))
}
Solution 3:[3]
I think the easiest thing is just to add a dummy field to the person struct with the XML tag.
A struct{}
element does not use any storage, I checked with unsafe.Sizeof()
.
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
Name string
Starsign string
XMLName struct{} `xml:"Person"`
}
func main() {
p := &person{Name: "John Smith", Starsign: "Capricorn"}
b, _ := xml.MarshalIndent(p, "", " ")
fmt.Println(string(b))
}
If you prefer to initialize the struct without using field names, it is necessary to add an item to initialize the empty struct, like this:
p := &person{"John Smith", "Capricorn", struct{}{}}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Matjam |
Solution 3 |