使用Golang进行XML解析(滥用实体属性)

时间:2016-10-24 08:52:15

标签: xml go xml-parsing

我有一个像这样格式化的XML,来自数据库转储

<table name="table1">
    <row>
        <col name="col1">value</col>
        <col name="col2">value</col>
        <col name="col3">value</col>
    </row>
   ...
</table>
<table name="table2">
    <row>
        <col name="col1">value</col>
        <col name="col2">value</col>
        <col name="col3">value</col>
        <col name="col4">value</col>
    </row>
   ...
</table>

我试图在Go中使用xml.Decode()函数解析它,但我无法处理这样的XML。 我尝试匹配在Golang中使用google搜索XML解析的一些示例,但没有任何东西适合这种格式(整个实体都有name个属性)。

更新

感谢您的回答!我想要达到的目的是:让我说我有一个名为&#34;用户&#34;以及一个名为&#34; categories&#34;的表格,我想从该XML开始创建尽可能多的UserCategory个对象。 根据您的第一个答案,我可以轻松地使用解析后的Table个对象,然后创建自己的对象,但我想知道我是否可以跳过它并直接解码我的对象。

3 个答案:

答案 0 :(得分:1)

我不知道您正在处理什么类型的问题,但您的XML实际上看起来像是一个XML值流,因此您应该将其解码为:

type Table struct {
    Name string `xml:"name,attr"`
    Rows []Row  `xml:"row"`
}

type Row struct {
    Cols []Col `xml:"col"`
}

type Col struct {
    Name  string `xml:"name,attr"`
    Value string `xml:",chardata"`
}

//...

var err error
dec := xml.NewDecoder(bytes.NewReader(b))
for err == nil {
    t := Table{}
    err = dec.Decode(&t)
    fmt.Printf("%v, %+v\n", err, t)
}

游乐场:https://play.golang.org/p/w4PqLv-Uc0

编辑:如果要将此类XML直接解码为适当的结构,则需要更复杂的机制。以下是用户的示例:

type User struct {
    ID      int
    Name    string
    Surname string
}

func (u *User) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var err error

    type col struct {
        Name  string `xml:"name,attr"`
        Value string `xml:",chardata"`
    }

    for {
        c := col{}
        err = d.Decode(&c)
        if err != nil {
            if err == io.EOF {
                break
            }

            return err
        }
        switch c.Name {
        case "id":
            u.ID, err = strconv.Atoi(c.Value)
            if err != nil {
                return err
            }
        case "name":
            u.Name = c.Value
            continue
        case "surname":
            u.Surname = c.Value
        }
    }

    return nil
}

type UserTable struct {
    Users []User `xml:"row"`
}

游乐场:https://play.golang.org/p/Hqta3Ngjo3

答案 1 :(得分:1)

xml.Unmarshal()函数的文档描述了XML文档和结构体之间的映射是如何工作的(由json.Marshal()的doc补充)。如果您不熟悉struct标签,请查看以下答案:What are the use(s) for tags in Go?

首先,您需要为XML文档建模。请注意,如果您没有围绕所有其他元素的包装XML元素,那么这不是1个XML文档,而是多个文档,例如您的案例。您有多个<table>个文档。

你可以像这样建模:

type Col struct {
    Name  string `xml:"name,attr"`
    Value string `xml:",chardata"`
}

type Row struct {
    Cols []Col `xml:"col"`
}

type Table struct {
    Name string `xml:"name,attr"`
    Rows []Row  `xml:"row"`
}

由于您有多个<table>元素,最简单的方法是创建xml.Decoder()并使用Decoder.Decode()调用分别解析每个元素Decoder.Decode()将尝试解析1个XML文档来自其来源阅读器)。

这是如何做到的:

d := xml.NewDecoder(strings.NewReader(data))
for {
    var table Table
    if err := d.Decode(&table); err != nil {
        fmt.Println(err)
        break
    }
    fmt.Printf("%+v\n", table)
}

如果来源[{1}}中没有其他数据,string将报告io.EOF。使用以下Decoder.Decode()字符串运行上述cod:

data

输出结果为:

const data = `<table name="table1">
    <row>
        <col name="col1">value1</col>
        <col name="col2">value2</col>
        <col name="col3">value3</col>
    </row>
    <row>
        <col name="col1">value4</col>
        <col name="col2">value5</col>
        <col name="col3">value6</col>
    </row>
</table>
<table name="table2">
    <row>
        <col name="col1">value7</col>
        <col name="col2">value8</col>
        <col name="col3">value9</col>
        <col name="col4">valueA</col>
    </row>
</table>`

Go Playground上尝试。

答案 2 :(得分:1)

要根据名称将每个字段解组为不同的类型,您需要获取每个开始元素标记,检查名称,然后根据需要进行解码。类似下面的内容(其中getName是一个返回name属性的函数;有关最小工作示例,请参阅playground链接):

d := xml.NewDecoder(…)
for {
    tok, err := d.Token()
    if err != nil {
        log.Fatal(err)
    }

    start, ok := tok.(xml.StartElement)
    if !ok {
        // This is chardata or some other token.
        // If it's bad XML, it will be caught on the next call to Token()
        continue
    }
    switch getName(start) {
    case "user":
        u := &User{}
        err = d.DecodeElement(u, &start)
        if err != nil {
            log.Fatal(err)
        }
        // Do whatever you want to do with your struct here.
        fmt.Printf("Decoded a user: %+v\n", u)
    case "category":
        u := &Category{}
        err = d.DecodeElement(u, &start)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Decoded a category: %+v\n", u)
    default:
        // If the name is unrecognized, skip the rest of the element.
        // We could also return an error
        d.Skip()
    }
}

https://play.golang.org/p/l7Vmj_8Igp