Go XML编组和根元素

huangapple go评论96阅读模式
英文:

Go XML Marshalling and the Root Element

问题

在Go语言中,你可以将一个结构体转换为XML格式,例如:

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

输出结果为:

<person>
   <Name>John Smith</Name>
   <Starsign>Capricorn</Starsign>
</person>

我的问题是,person类型是小写的“p”,因为我希望它对包私有。但是我更希望XML元素是大写的:“”。可以使用标签(例如xml:"name")来将结构体中的字段转换为其他名称,但是对于结构体类型似乎没有这个选项。

我有一个使用模板的解决方法,但是知道更好的答案会更好。

英文:

In Go, you can marshall a struct to XML, e.g.:

package main

import (
	&quot;encoding/xml&quot;
	&quot;fmt&quot;
	)

type person struct {
	Name string
	Starsign string
}

func main() {
    p := &amp;person{&quot;John Smith&quot;, &quot;Capricorn&quot;}
    b,_ := xml.MarshalIndent(p,&quot;&quot;,&quot;   &quot;)
    fmt.Println(string(b))
}

produces output:

&lt;person&gt;
   &lt;Name&gt;John Smith&lt;/Name&gt;
   &lt;Starsign&gt;Capricorn&lt;/Starsign&gt;
&lt;/person&gt;

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: &lt;Person&gt;. 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.

答案1

得分: 14

根据encoding/xml.Marshal文档:

> XML元素的名称按照以下优先顺序确定:

>- 如果数据是一个结构体,则使用XMLName字段上的标签

  • 如果数据是xml.Name类型,则使用XMLName字段的值
  • 如果数据是通过结构体字段获取的,则使用该字段的标签
  • 如果数据是通过结构体字段获取的,则使用该字段的名称
  • 如果数据是通过编组类型获取的,则使用编组类型的名称

您可以在结构体的XMLName字段上使用标签来覆盖person结构体的XML标签名称。为了避免将其放在实际的person结构体中,您可以创建一个嵌入了您正在编组的person结构体的匿名结构体。

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))
}
英文:

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 (
    &quot;encoding/xml&quot;
    &quot;fmt&quot;
)

type person struct {
    Name		string
    Starsign	string
}

func marshalPerson(p person) ([]byte, error) {
    tmp := struct {
        person
        XMLName	struct{}    `xml:&quot;Person&quot;`
    }{person: p}

    return xml.MarshalIndent(tmp, &quot;&quot;, &quot;   &quot;)
}

func main() {
    p := person{&quot;John Smith&quot;, &quot;Capricorn&quot;}
    b, _ := marshalPerson(p)
    fmt.Println(string(b))
}

答案2

得分: 5

这也可以工作,虽然我不认为它特别漂亮。

然而,对我来说,这种方法比5年前的其他解决方案更直接。

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))
}
英文:

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 (
    &quot;encoding/xml&quot;
    &quot;fmt&quot;
    )

type person struct {
    XMLName xml.Name
    Name string
    Starsign string
}

func main() {
    p := &amp;person{xml.Name{Local: &quot;Person&quot;}, &quot;John Smith&quot;, &quot;Capricorn&quot;}
    b,_ := xml.MarshalIndent(p,&quot;&quot;,&quot;   &quot;)
    fmt.Println(string(b))
}

答案3

得分: 4

我认为最简单的方法就是向person结构体添加一个带有XML标签的虚拟字段。

一个struct{}元素不使用任何存储空间,我用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))
}

<kbd>go playground</kbd>

如果你喜欢在不使用字段名的情况下初始化结构体,需要添加一个项目来初始化空结构体,像这样:

p := &person{"John Smith", "Capricorn", struct{}{}}
英文:

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 (
	&quot;encoding/xml&quot;
	&quot;fmt&quot;
)

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

func main() {
	p := &amp;person{Name: &quot;John Smith&quot;, Starsign: &quot;Capricorn&quot;}
	b, _ := xml.MarshalIndent(p, &quot;&quot;, &quot;   &quot;)
	fmt.Println(string(b))
}

<kbd>go playground</kbd>

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 := &amp;person{&quot;John Smith&quot;, &quot;Capricorn&quot;, struct{}{}}

huangapple
  • 本文由 发表于 2012年9月13日 11:14:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/12398925.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定