10

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.

4

3 回答 3

13

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))
}
于 2012-09-13T04:02:08.357 回答
4

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))
}
于 2017-09-13T19:57:12.037 回答
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().

The only drawback is that e need to add a struct{}{} initial value for that field, in this example.

package main

import (
    "encoding/xml"
    "fmt"
    )

type person struct {
    Name string
    Starsign string
    XMLName struct{} `xml:Person`
}

func main() {
    p := &person{"John Smith", "Capricorn", struct{}{}}
    b,_ := xml.MarshalIndent(p,"","   ")
    fmt.Println(string(b))
}
于 2021-03-17T10:01:07.387 回答