I have the following struct:
type CustomAttribute struct {
Id string `xml:"attribute-id,attr,omitempty"`
Values []string `xml:"value,omitempty"`
}
type Store struct {
XMLName xml.Name `xml:"store"`
Id string `xml:"store-id,attr,omitempty"`
Name string `xml:"name,omitempty"`
Address1 string `xml:"address1,omitempty"`
Address2 string `xml:"address2,omitempty"`
City string `xml:"city,omitempty"`
PostalCode string `xml:"postal-code,omitempty"`
StateCode string `xml:"state-code,omitempty"`
CountryCode string `xml:"country-code,omitempty"`
Phone string `xml:"phone,omitempty"`
Lat float64 `xml:"latitude,omitempty"`
Lng float64 `xml:"longitude,omitempty"`
CustomAttributes []CustomAttribute `xml:"custom-attributes>custom-attribute,omitempty"`
}
and then I initialise the struct as follows:
store := &Store{
Id: storeId,
Name: row[4],
Address1: row[5],
Address2: row[6],
City: row[7],
PostalCode: row[9],
StateCode: row[8],
CountryCode: row[11],
Phone: row[10],
}
So the CustomAttributes array is always empty, and len(store.CustomAttributes) is 0 so any idea why the generated XML still contains the empty "custom-attributes" tag?
<custom-attributes></custom-attributes>
One solution is to make the CustomAttributes
field a pointer. It will be omitted when the value is nil
. Look for "zero values" in Marshal documentation.
package main
import (
"encoding/xml"
"fmt"
"log"
)
type Store struct {
XMLName xml.Name `xml:"store"`
CustomAttributes *[]CustomAttribute `xml:"custom-attributes>custom-attribute,omitempty"`
}
type CustomAttribute struct {
Id string `xml:"attribute-id,attr,omitempty"`
Values []string `xml:"value,omitempty"`
}
func print(store *Store) {
data, err := xml.Marshal(store)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
}
func main() {
print(&Store{})
print(&Store{
CustomAttributes: &[]CustomAttribute{},
})
print(&Store{
CustomAttributes: &[]CustomAttribute{
{Id: "hello"},
},
})
}
Playground
I think what happens here is that since you specified the name of the element as being nested, custom-attributes>custom-attribute
that sort of implied the "outer" ("intermediate"?, "container"?) element, custom-attributes
should exist — in part because of nothing prevents you from tagging any number of other fields with names including the same outer element — like custom-attributes>foobar
.
Tracking the case none of such fields were marshaled and hence their outer element should not be used is probably too much for the marshaler which — I suppose — is explicitly written to keep as less context as possible while it works.
Hence while I understand you being puzzled, I think this behaviour is understandable once you squint at it a bit more long.
As to what to do about solving it, I'd personally try to be more explicit
and wrapped your slice into a struct
type, like with
type CustomAttributes struct {
XMLName xml.Name `xml:"custom-attributes"`
Items []CustomAttribute `xml:"custom-attributes>custom-attribute"`
}
and then would have a custom marshaler on it:
func (cas CustomAttributes) MarshalXML(e *xml.Encoder,
start xml.StartElement) (err error) {
if len(cas.Items) == 0 {
return nil
}
err = e.EncodeToken(start)
if err != nil {
return
}
err = e.Encode(cas.Items)
if err != nil {
return
}
return e.EncodeToken(xml.EndElement{
Name: start.Name,
})
}
Playground link.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With