'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))
}

go playground

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