go-plist
go-plist copied to clipboard
Embedded structs' fields are not omitted
Unlike Go's encoding/json, it would appear that go-plist still marshals the fields of embedded structs even if those structs are nil and the omitempty tag is specified. Consider the following example program:
package main
import (
"fmt"
"log"
"howett.net/plist"
)
type Location struct {
Name string
Type string
*Restaurant `plist:",omitempty"`
*Store `plist:",omitempty"`
}
type Restaurant struct {
Menu string
}
type Store struct {
Merchandise string
}
func main() {
location := Location{
Name: "Taqueria Cancun",
Type: "Restaurant",
Restaurant: &Restaurant{
Menu: "Tacos",
},
}
locationPlist, err := plist.MarshalIndent(&location, plist.XMLFormat, "\t")
if err != nil {
log.Fatalf("marshal location: %v", err)
}
fmt.Println(string(locationPlist))
}
The resulting property list is
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Menu</key>
<string>Tacos</string>
<key>Merchandise</key>
<string/>
<key>Name</key>
<string>Taqueria Cancun</string>
<key>Type</key>
<string>Restaurant</string>
</dict>
</plist>
This plist representation contains an empty Merchandise string which is not pertinent to a Restaurant and which I'd like to omit. The encoding/json library does this as seen from the example below:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Location struct {
Name string
Type string
*Restaurant
*Store
}
type Restaurant struct {
Menu string
}
type Store struct {
Merchandise string
}
func main() {
location := Location{
Name: "Taqueria Cancun",
Type: "Restaurant",
Restaurant: &Restaurant{
Menu: "Tacos",
},
}
locationJSON, err := json.Marshal(&location)
if err != nil {
log.Fatalf("marshal location: %v", err)
}
fmt.Println(string(locationJSON))
}
Running this (and piping the output to jq) results in
{
"Name": "Taqueria Cancun",
"Type": "Restaurant",
"Menu": "Tacos"
}
where the Merchandise key is absent. Should the go-plist library not also have this behavior?
One way to work around this (which is probably better pattern in general) is to embed the Location struct into the Restaurant and Store structs rather than vice versa:
package main
import (
"fmt"
"log"
"howett.net/plist"
)
type Location struct {
Name string
Type string
}
type Restaurant struct {
Location
Menu string
}
type Store struct {
Location
Merchandise string
}
func main() {
restaurant := Restaurant{
Location: Location{
Name: "Taqueria Cancun",
Type: "Restaurant",
},
Menu: "Tacos",
}
locationPlist, err := plist.MarshalIndent(&restaurant, plist.XMLFormat, "\t")
if err != nil {
log.Fatalf("marshal location: %v", err)
}
fmt.Println(string(locationPlist))
}
This results in the desired output,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Menu</key>
<string>Tacos</string>
<key>Name</key>
<string>Taqueria Cancun</string>
<key>Type</key>
<string>Restaurant</string>
</dict>
</plist>
However, my point still stands that go-plist's behavior deviates from that of encoding/json in this regard.
Thanks for the report, and comprehensive investigation! I'm a bit behind on my personal repositories, but I'll try to get to this this week. You're right - this should work like encoding/json. There's a chance that this behavior has changed over time and I missed it, or I never mimicked encoding/json properly. :smile: