英文:
Parsing sub-element's attribute directly into Go struct
问题
在使用Go解析XML时,我该如何将嵌套元素的属性直接读入到我的结构体中?
我的XML如下所示,它是OpenStreetMap格式的一部分:
<way id="123">
<nd ref="101"/>
<!-- Lots of nd elements repeated -->
<nd ref="109"/>
</way>
我有以下结构体:
type Way struct {
Nodes []NodeRef `xml:"nd"`
}
以及:
type NodeRef struct {
Ref int `xml:"ref,attr"`
}
但我希望能够直接使用以下结构体:
type Way struct {
Nodes []int `???`
}
关于解组的文档对我没有帮助。我尝试使用xml:"nd>ref,attr"
,但会出现"chain not valid with attr flag"的错误。
请参考下面的示例代码。在Go Playground中运行代码
package main
import (
"encoding/xml"
"fmt"
"io"
"os"
"strings"
)
func main() {
data := `
<way id="5090250">
<nd ref="822403"/>
<nd ref="21533912"/>
<nd ref="821601"/>
<nd ref="21533910"/>
<nd ref="135791608"/>
<nd ref="333725784"/>
<nd ref="333725781"/>
<nd ref="333725774"/>
<nd ref="333725776"/>
<nd ref="823771"/>
</way>
`
r := strings.NewReader(data)
way, err := ReadWay(r)
if err != nil {
fmt.Println("Could not read", err)
os.Exit(1)
}
fmt.Println(way)
}
// 我想摆脱这个嵌套的结构体。
type NodeRef struct {
Ref int `xml:"ref,attr"`
}
type Way struct {
ID int `xml:"id,attr"`
// 我该如何将所有的<nd ref="123"/>直接写入到Nodes []int中?
Nodes []NodeRef `xml:"nd"`
}
func ReadWay(reader io.Reader) (Way, error) {
var way Way
if err := xml.NewDecoder(reader).Decode(&way); err != nil {
return way, err // 为什么我不能返回nil, err?
}
return way, nil
}
英文:
In parsing XML with Go, how can I read a nested element's attribute directly into my struct?
My XML looks like this below. It is part of the OpenStreetMap format:
<way id="123" >
<nd ref="101"/>
<!-- Lots of nd elements repeated -->
<nd ref="109"/>
</way>
I have
type Way struct {
Nodes []NodeRef `xml:"nd"`
}
with
type NodeRef struct {
Ref int `xml:"ref,attr"`
}
but I would like to be able to do
type Way struct {
Nodes []int `???`
}
directly.
The documentation on Unmarshalling didn't help me. I've tried using xml:"nd>ref,attr"
but that fails with "chain not valid with attr flag".
Please see the below example code. Run the code in Go Playground
package main
import (
"encoding/xml"
"fmt"
"io"
"os"
"strings"
)
func main() {
data := `
<way id="5090250" >
<nd ref="822403"/>
<nd ref="21533912"/>
<nd ref="821601"/>
<nd ref="21533910"/>
<nd ref="135791608"/>
<nd ref="333725784"/>
<nd ref="333725781"/>
<nd ref="333725774"/>
<nd ref="333725776"/>
<nd ref="823771"/>
</way>
`
r := strings.NewReader(data)
way, err := ReadWay(r)
if err != nil {
fmt.Println("Could not read", err)
os.Exit(1)
}
fmt.Println(way)
}
// I'd like to get rid of this nested struct.
type NodeRef struct {
Ref int `xml:"ref,attr"`
}
type Way struct {
ID int `xml:"id,attr"`
// How can I write all <nd ref="123"/> directly into Nodes []int?
Nodes []NodeRef `xml:"nd"`
}
func ReadWay(reader io.Reader) (Way, error) {
var way Way
if err := xml.NewDecoder(reader).Decode(&way); err != nil {
return way, err // Why can't I return nil, err?
}
return way, nil
}
答案1
得分: 3
简而言之,你不能直接这样做。绕过XML结构的常见方法是实现xml.Unmarshaler
接口。这里有一个示例:
type Way struct {
ID int
Nodes []int
}
func (w *Way) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var payload struct {
ID int `xml:"id,attr"`
Nodes []struct {
Ref int `xml:"ref,attr"`
} `xml:"nd"`
}
err := d.DecodeElement(&payload, &start)
if err != nil {
return err
}
w.ID = payload.ID
w.Nodes = make([]int, 0, len(payload.Nodes))
for _, node := range payload.Nodes {
w.Nodes = append(w.Nodes, node.Ref)
}
return nil
}
在这里,payload
变量的结构与你的XML数据相对应,而Way
结构体则按照你的要求进行建模。一旦解码完成,你可以根据需要使用它来初始化Way
变量。
附注:为什么不能返回nil, err
?
你不能返回nil
,因为Way
既不是指针类型也不是接口类型,因此nil
不是它的有效值。
英文:
In short, you can't do that directly. The common pattern to bypass the XML structure is to implement the xml.Unmarshaler
interface. Here is an example:
type Way struct {
ID int
Nodes []int
}
func (w *Way) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var payload struct {
ID int `xml:"id,attr"`
Nodes []struct {
Ref int `xml:"ref,attr"`
} `xml:"nd"`
}
err := d.DecodeElement(&payload, &start)
if err != nil {
return err
}
w.ID = payload.ID
w.Nodes = make([]int, 0, len(payload.Nodes))
for _, node := range payload.Nodes {
w.Nodes = append(w.Nodes, node.Ref)
}
return nil
}
Here, the payload
variable is modeled after your XML data, while the Way
struct is modeled as you want it to be. Once the payload is decoded, you can use it to initialize the Way
variable as you want…
Side note: // Why can't I return nil, err?
You can't return nil
because Way
isn't either a pointer or an interface, thus nil
isn't a valid value for it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论