我有一个像这样格式化的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开始创建尽可能多的User
和Category
个对象。
根据您的第一个答案,我可以轻松地使用解析后的Table
个对象,然后创建自己的对象,但我想知道我是否可以跳过它并直接解码我的对象。
答案 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"`
}
答案 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()
}
}