I'm relatively new to working with golang and I could use some advice on populating a struct tree from an XML file. edit: I fixed an inconsistency with the XML structure vs the struct definitions. Updated the playground link; the full code with a sample XML is at http://play.golang.org/p/1ymyESO2jp .
The XML file has a combination of attributes and values (chardata)
<?xml version="1.0" encoding="UTF-8"?>
<system name="SystemA123" enabled="true" open="9" close="15" timeZone="America/Denver" freq="4h" dailyMax="1" override="false">
<hosts>
<host address="10.1.2.3">
<command>"free -mo</command>
<command>"cat /proc/cpuinfo | grep processor"</command>
<command>"ifconfig eth0 down"</command>
<command>"shutdown -r now"</command>
<command>"cat /proc/loadavg"</command>
</host>
<host address="10.1.2.4">
... more commands>command elements
</host>
I've build the corresponding structs like so:
type SystemConfig struct {
XMLName xml.Name `xml:"system"`
SysName string `xml:"name,attr"`
Enabled bool `xml:"enabled,attr"`
OpenHour int `xml:"open,attr"`
CloseHour int `xml:"close,attr"`
TimeZone string `xml:"timeZone,attr"`
Frequency string `xml:"freq,attr"` //will use time.ParseDuration to read the interval specified here
DailyMax int `xml:"dailyMax,attr"`
Override bool `xml:"override,attr"`
Hosts []*Hosts `xml:"hosts"`
}
type Hosts struct {
XMLName xml.Name `xml:"hosts"`
Host []*Host `xml:host"`
}
type Host struct {
XMLName xml.Name `xml:"host"`
IPaddr string `xml:"address,attr"`
Commands []*HostCommands `xml:"command"`
}
type HostCommands struct {
XMLName xml.Name `xml:"command"`
Command string `xml:",chardata"`
}
and I'm reading the XML file into the structs with this code:
var sc *SystemConfig
xmlConf, err := os.Open(appConfFile)
defer xmlConf.Close()
sc, err = ReadSystemConfig(xmlConf)
with this method definition for ReadSystemConfig
func ReadSystemConfig(reader io.Reader) (*SystemConfig, error) {
sysConf := &SystemConfig{}
decoder := xml.NewDecoder(reader)
if err := decoder.Decode(sysConf); err != nil {
//fmt.Println("error decoding sysConf in ReadSystemConfig: %v", err)
return nil, err
}
return sysConf, nil
}
SystemConfigHostsfmt.Printf("first IP address: %v
", sc.Hosts[0].Host[0].IPaddr
Hi:app pd$ ./app -f ../config/conf.xml
SystemConfig:SysName SystemA123
SystemConfig:Enabled true
SystemConfig:OpenHour 9
SystemConfig:CloseHour 15
SystemConfig:TimeZone America/Denver
SystemConfig:Frequency 4h
SystemConfig:DailyMax 1
SystemConfig:Override false
panic: runtime error: index out of range
goroutine 1 [running]:
runtime.panic(0xd9cc0, 0x1fecf7)
/usr/local/go/src/pkg/runtime/panic.c:266 +0xb6
main.main()
/Users/pd/golang/src/local/boxofsand/app/boxofsand.go:95 +0x8ef
I feel like I have the structs setup properly (happy to take advice if I don't); I have tried removing the tags and modifying the code to start from the tags, but I wasn't able to pull any data that way, either (same index out of range error). So, I'm confident that I have borked up 2-3 lines of code here, but I can't find a realistic example online that reads relatively simple, multi-level XML from a file.
Lastly, I'd be open to advice on doing this configuration in JSON instead. I didn't choose that initially because I thought the deeper nesting would be harder to read; also, a lot of what I work with on other projects is XML-based, so I'd like to learn something here that I can apply to those other (for now, Java-based) projects.
As always, thanks in advance for any advice or help you're willing to lend.
edit: I saw a consistency issue in the XML skeleton vs the struct definitions. I modified the XML to suit the structs by removing the tagset