golang麻烦遍历,从文件填充xml

时间:2014-05-16 16:25:14

标签: xml go

我对使用golang相对较新,我可以使用一些建议从XML文件中填充结构树。 编辑:我修复了XML结构与结构定义的不一致。更新了游乐场链接;带有示例XML的完整代码位于http://play.golang.org/p/1ymyESO2jp

XML文件具有属性和值(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>

我已经构建了相应的结构:

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"`
}

我正在使用以下代码将XML文件读入结构:

var sc *SystemConfig
xmlConf, err := os.Open(appConfFile)
defer xmlConf.Close()
sc, err = ReadSystemConfig(xmlConf)

使用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
}

当我按原样运行时,包括一些fmt.Printf语句来验证数据是否正确加载,SystemConfig属性是否正确加载,但我无法获得任何Hosts数据加载。在我要求fmt.Printf("first IP address: %v\n", 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

我觉得我的结构设置得当(如果不这样的话,很乐意接受建议);我已经尝试删除标签并修改代码以从标签开始,但我也无法以这种方式提取任何数据(相同的索引超出范围错误)。所以,我确信我在这里已经填充了2-3行代码,但我找不到一个在线的实际示例,它从文件中读取相对简单的多级XML。

最后,我愿意接受有关在JSON中进行此配置的建议。我最初没有选择,因为我认为更深的嵌套会更难读;另外,我在其他项目中使用的很多东西都是基于XML的,所以我想在这里学到一些东西,我可以应用于其他项目(现在是基于Java的)项目。

与往常一样,提前感谢您愿意放贷的任何建议或帮助。

编辑:我发现XML框架与结构定义存在一致性问题。我通过删除标记集

修改了XML以适应结构

2 个答案:

答案 0 :(得分:3)

帕特里克,看起来你的结构有一些不一致(当我第一次尝试使用Go来解析嵌套的xml / json时,我遇到了同样的问题)。主要是因为你有Host个对象的数组。我调整了你的结构,这对我来说似乎很好解析:

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     []Host   `xml:"hosts>host"`
}

type Host struct {
    XMLName  xml.Name      `xml:"host"`
    IPaddr   string        `xml:"address,attr"`
    Commands []HostCommand `xml:"commands>command"`
}

type HostCommand struct {
    XMLName xml.Name `xml:"command"`
    Command string   `xml:",chardata"`
}

作为补充说明,我在Go中编码问题时找到了最有用的东西之一,构建结构,将一些模拟数据放入其中,然后吐出数据并查看Go如何格式化它。从那里,通常很容易看出你出错的地方。

例如,这里是我用来迭代并找出你的结构有什么问题的代码:

package main

import (
    "encoding/xml"
    "fmt"
)

var (
    xmlData = `... your xml chunk ...`
)

// ... structs ...

func main() {
    conf, _ := ReadSystemConfig(xmlData)
    fmt.Printf("%#v\n", conf)
    data, _ := WriteSystemConfig(conf)
    fmt.Printf("%#v\n", data)
}

func ReadSystemConfig(data string) (*SystemConfig, error) {
    sysConf := &SystemConfig{}
    if err := xml.Unmarshal([]byte(data), sysConf); err != nil {
        return nil, err
    }
    return sysConf, nil
}

func WriteSystemConfig(sysConf *SystemConfig) (string, error) {
    dataBytes, err := xml.Marshal(sysConf)
    if err != nil {
        return "", err
    }
    return string(dataBytes), nil
}

有了这个,我能够读取你的xml并吐出来看看Go能够解析什么,然后我猜了几次(之前在Go中完成了xml解析),并迭代直到所有的数据回来了。

希望这有帮助!

答案 1 :(得分:0)

我认为标记集是无关的,并将它们从XML结构中删除,同时删除了Hosts结构。当我进行此更改时,XML数据已正确加载。

我意识到我正在使用一个名为Host of type [] * Host的变量,这可能会让运行时感到困惑。

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     []*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"`
}