`omitempty`和指针字段在Go的标准`xml`包中的作用是什么?

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

`,omitempty` and pointer fields in Go's standard `xml` package

问题

当将结构体的布尔字段编组为XML时,omitempty选项对大多数情况来说并不是很有用——在Go语言中,false是布尔变量的零值,因此在编组时,值为false的布尔字段会被忽略掉。最常建议的解决方案似乎是使用指针,这样可以指示值是否存在。以下是我对这个想法的基本实现:

package main

import (
	"encoding/xml"
	"fmt"
)

type Person struct {
	XMLName   xml.Name `xml:"person"`
	IsMarried *bool    `xml:"married"`           // 必需字段。
	IsRetired *bool    `xml:"retired,omitempty"` // 可选字段。
}

func boolPointer(b bool) *bool {
	return &b
}

func printPersonXml(person Person) {
	output, err := xml.MarshalIndent(person, "  ", "    ")
	if err != nil {
		fmt.Printf("error: %v\n", err)
	} else {
		fmt.Println(string(output))
	}
}

func main() {
	person := Person{
		IsMarried: boolPointer(true),
		IsRetired: nil,
	}
	printPersonXml(person)
}

这将按预期工作并产生以下输出:

  <person>
      <married>true</married>
  </person>

然而,似乎在这种情况下,omitempty选项完全失去了意义。任何具有nil值的字段都不会包含在生成的XML代码中。例如,如果我将main()的内容更改为:

person := Person{
	IsMarried: nil,
	IsRetired: nil,
}
printPersonXml(person)

输出将变为:

  <person></person>

尽管如此,我更希望输出为:

  <person>
      <married></married>
  </person>

正如这里所指出的,这可能是预期的行为:“Marshal通过编组指针指向的值,或者如果指针为nil,则不写入任何内容。”

然而,是否可能使用标准的xml包来实现我期望的行为呢?如果可以,是否需要引入新类型和自定义的MarshalXML()方法呢?

尽管在这里我专注于布尔变量,但我也希望将这种方法扩展到其他基本类型的指针上。

英文:

When marshalling a boolean field of a struct to XML, the ,omitempty option is not very useful for most purposes—false is a zero value of boolean variables in Go and, as expected, boolean ,omitempty fields with value of false are ignored when marshalling. The most often suggested solution seems to be using pointers which would allow to indicate whether a value is present at all. Here is my basic implementation of this idea:

package main

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

type Person struct {
	XMLName   xml.Name `xml:&quot;person&quot;`
	IsMarried *bool    `xml:&quot;married&quot;`           // Required field.
	IsRetired *bool    `xml:&quot;retired,omitempty&quot;` // Optional field.
}

func boolPointer(b bool) *bool {
	return &amp;b
}

func printPersonXml(person Person) {
	output, err := xml.MarshalIndent(person, &quot;  &quot;, &quot;    &quot;)
	if err != nil {
		fmt.Printf(&quot;error: %v\n&quot;, err)
	} else {
		fmt.Println(string(output))
	}
}

func main() {
	person := Person{
		IsMarried: boolPointer(true),
		IsRetired: nil,
	}
	printPersonXml(person)
}

This works as expected and produces the output

  &lt;person&gt;
      &lt;married&gt;true&lt;/married&gt;
  &lt;/person&gt;

However, it seems that in this case ,omitempty option loses its meaning entirely. Any field with nil value will not be included in the produced XML code. For example, if I change the contents of main() to

person := Person{
	IsMarried: nil,
	IsRetired: nil,
}
printPersonXml(person)

the output becomes

  &lt;person&gt;&lt;/person&gt;

even though I would prefer

  &lt;person&gt;
      &lt;married&gt;&lt;/married&gt;
  &lt;/person&gt;

As noted here, this is probably the expected behaviour: "Marshal handles a pointer by marshaling the value it points at or, if the pointer is nil, by writing nothing."

However, is it possible to achieve my preferred behaviour using standard xml package? If yes, would that require introducing a new type and a custom MarshalXML() method for it?

Although here I focus on boolean variables for obvious reasons, I would like to extend this approach to pointers of other basic types as well.

答案1

得分: 3

“使用标准的xml包能否实现我期望的行为?如果可以,是否需要引入新的类型和自定义的MarshalXML()方法?” - 是的,可以实现,并且需要引入新的类型和自定义的MarshalXML()方法。

例如:

type Bool struct {
	Bool    bool
	IsValid bool
}

func (b Bool) MarshalXML(e *xml.Encoder, se xml.StartElement) error {
	if b.IsValid {
		return e.EncodeElement(b.Bool, se)
	}
	return e.EncodeElement("", se)
}

type OptionalBool struct {
	Bool    bool
	IsValid bool
}

func (b OptionalBool) MarshalXML(e *xml.Encoder, se xml.StartElement) error {
	if b.IsValid {
		return e.EncodeElement(b.Bool, se)
	}
	return nil
}

https://play.golang.org/p/C2fuBfv69Ny

英文:

"is it possible to achieve my preferred behaviour using standard xml package? If yes, would that require introducing a new type and a custom MarshalXML() method for it?" -- Yes, and yes.

For example:

type Bool struct {
	Bool    bool
	IsValid bool
}

func (b Bool) MarshalXML(e *xml.Encoder, se xml.StartElement) error {
	if b.IsValid {
		return e.EncodeElement(b.Bool, se)
	}
	return e.EncodeElement(&quot;&quot;, se)
}

type OptionalBool struct {
	Bool    bool
	IsValid bool
}

func (b OptionalBool) MarshalXML(e *xml.Encoder, se xml.StartElement) error {
	if b.IsValid {
		return e.EncodeElement(b.Bool, se)
	}
	return nil
}

https://play.golang.org/p/C2fuBfv69Ny

huangapple
  • 本文由 发表于 2021年7月10日 20:08:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/68327573.html
匿名

发表评论

匿名网友

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

确定