Reputation: 51
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 (
"encoding/xml"
"fmt"
)
type Person struct {
XMLName xml.Name `xml:"person"`
IsMarried *bool `xml:"married"` // Required field.
IsRetired *bool `xml:"retired,omitempty"` // Optional field.
}
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)
}
This works as expected and produces the output
<person>
<married>true</married>
</person>
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
<person></person>
even though I would prefer
<person>
<married></married>
</person>
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.
Upvotes: 2
Views: 758
Reputation: 38203
"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("", 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
Upvotes: 3