英文:
Parse repeating fields in xml
问题
我收到了一个包含多个条目的XML响应,其中包含<Rights></Rights>
块。它有几个<Name></Name>
和<Access></Access>
字段。
<?xml version="1.0" encoding="UTF-8"?>
<SessionInfo>
<SID>000000000000</SID>
<Challenge>1337</Challenge>
<BlockTime>0</BlockTime>
<Rights>
<Name>Dial</Name>
<Access>2</Access>
<Name>App</Name>
<Access>2</Access>
<Name>HomeAuto</Name>
<Access>2</Access>
<Name>BoxAdmin</Name>
<Access>2</Access>
<Name>Phone</Name>
<Access>2</Access>
<Name>NAS</Name>
<Access>2</Access>
</Rights>
</SessionInfo>
我想将其转换为一个rights结构。
type sessionInfo struct {
XMLName xml.Name `xml:"SessionInfo"`
SID string `xml:"SID"`
Challenge string `xml:"Challenge"`
BlockTime uint `xml:"BlockTime"`
Rights []rights `xml:"Rights"`
}
type rights struct {
Name string `xml:"Name"`
Access int `xml:"Access"`
}
不幸的是,它只会将最后一个元素写入数组。在Go中是否有可能在不编写自己的解码器的情况下实现这一点?
<SessionInfo>
<SID>000000000000</SID>
<Challenge>1337</Challenge>
<BlockTime>0</BlockTime>
<Rights>
<Name>NAS</Name>
<Access>2</Access>
</Rights>
</SessionInfo>
你可以在这里进行测试:https://play.golang.org/p/29I2GPttOz
英文:
I got an xml response that contains multiple entries in the <Rights></Rights>
block. It has several <Name></Name>
and <Access></Access>
fields.
<?xml version="1.0" encoding="UTF-8"?>
<SessionInfo>
<SID>000000000000</SID>
<Challenge>1337</Challenge>
<BlockTime>0</BlockTime>
<Rights>
<Name>Dial</Name>
<Access>2</Access>
<Name>App</Name>
<Access>2</Access>
<Name>HomeAuto</Name>
<Access>2</Access>
<Name>BoxAdmin</Name>
<Access>2</Access>
<Name>Phone</Name>
<Access>2</Access>
<Name>NAS</Name>
<Access>2</Access>
</Rights>
</SessionInfo>
I would like to have this converted into a rights struct.
type sessionInfo struct {
XMLName xml.Name `xml:"SessionInfo"`
SID string `xml:"SID"`
Challenge string `xml:"Challenge"`
BlockTime uint `xml:"BlockTime"`
Rights []rights `xml:"Rights"`
}
type rights struct {
Name string `xml:"Name"`
Access int `xml:"Access"`
}
Unfortunatelly it does only write the last Element into the array. Is it somehow possible to do this in Go without the need to write an own decoder?
<SessionInfo>
<SID>000000000000</SID>
<Challenge>1337</Challenge>
<BlockTime>0</BlockTime>
<Rights>
<Name>NAS</Name>
<Access>2</Access>
</Rights>
</SessionInfo>
You can test this here: https://play.golang.org/p/29I2GPttOz
答案1
得分: 5
由于XML文档的布局,内置的编组规则无法将数据解码为您提供的数据类型。
以下是一个可能适用于您的文档的编组器和解组器实现:
package main
import (
"encoding/xml"
"fmt"
"io"
"log"
"strconv"
)
var data = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<SessionInfo>
<SID>000000000000</SID>
<Challenge>1337</Challenge>
<BlockTime>0</BlockTime>
<Rights>
<Name>Dial</Name>
<Access>2</Access>
<Name>App</Name>
<Access>2</Access>
<Name>HomeAuto</Name>
<Access>2</Access>
<Name>BoxAdmin</Name>
<Access>2</Access>
<Name>Phone</Name>
<Access>2</Access>
<Name>NAS</Name>
<Access>2</Access>
</Rights>
</SessionInfo>`)
type sessionInfo struct {
XMLName xml.Name `xml:"SessionInfo"`
SID string `xml:"SID"`
Challenge string `xml:"Challenge"`
BlockTime uint `xml:"BlockTime"`
Rights *rights `xml:"Rights"`
}
type rights struct {
Rights []*right
}
type NameElement struct {
XMLName xml.Name `xml:"Name"`
Value string `xml:",chardata"`
}
type AccessElement struct {
XMLName xml.Name `xml:"Access"`
Value string `xml:",chardata"`
}
func (r *rights) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
var name NameElement
var access AccessElement
if err := d.Decode(&name); err != nil {
if err == io.EOF {
break
}
return err
}
if err := d.Decode(&access); err != nil {
return err
}
value, err := strconv.Atoi(access.Value)
if err != nil {
return err
}
r.Rights = append(r.Rights, &right{
Name: name.Value,
Access: value,
})
}
return nil
}
func (r *rights) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
parentName := xml.Name{
Local: "Rights",
}
parentStart := xml.StartElement{
Name: parentName,
}
if err := e.EncodeToken(parentStart); err != nil {
return err
}
for _, right := range r.Rights {
name := NameElement{
Value: right.Name,
}
value := AccessElement{
Value: strconv.Itoa(right.Access),
}
if err := e.Encode(&name); err != nil {
return err
}
if err := e.Encode(&value); err != nil {
return err
}
}
parentEnd := xml.EndElement{
Name: parentName,
}
if err := e.EncodeToken(parentEnd); err != nil {
return err
}
return nil
}
type right struct {
Name string
Access int
}
func main() {
var result sessionInfo
if err := xml.Unmarshal(data, &result); err != nil {
log.Fatalln(err)
}
if out, err := xml.MarshalIndent(result, "", " "); err != nil {
log.Fatalln(err)
} else {
fmt.Println(string(out))
}
}
链接:https://play.golang.org/p/MK0RCfJo0a
英文:
Due to the layout of the XML document, the built-in marshaling rules cannot decode the data into your given data types.
Below is a marshaler and unmarshaler implementation that should work for your document:
package main
import (
"encoding/xml"
"fmt"
"io"
"log"
"strconv"
)
var data = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<SessionInfo>
<SID>000000000000</SID>
<Challenge>1337</Challenge>
<BlockTime>0</BlockTime>
<Rights>
<Name>Dial</Name>
<Access>2</Access>
<Name>App</Name>
<Access>2</Access>
<Name>HomeAuto</Name>
<Access>2</Access>
<Name>BoxAdmin</Name>
<Access>2</Access>
<Name>Phone</Name>
<Access>2</Access>
<Name>NAS</Name>
<Access>2</Access>
</Rights>
</SessionInfo>`)
type sessionInfo struct {
XMLName xml.Name `xml:"SessionInfo"`
SID string `xml:"SID"`
Challenge string `xml:"Challenge"`
BlockTime uint `xml:"BlockTime"`
Rights *rights `xml:"Rights"`
}
type rights struct {
Rights []*right
}
type NameElement struct {
XMLName xml.Name `xml:"Name"`
Value string `xml:",chardata"`
}
type AccessElement struct {
XMLName xml.Name `xml:"Access"`
Value string `xml:",chardata"`
}
func (r *rights) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
var name NameElement
var access AccessElement
if err := d.Decode(&name); err != nil {
if err == io.EOF {
break
}
return err
}
if err := d.Decode(&access); err != nil {
return err
}
value, err := strconv.Atoi(access.Value)
if err != nil {
return err
}
r.Rights = append(r.Rights, &right{
Name: name.Value,
Access: value,
})
}
return nil
}
func (r *rights) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
parentName := xml.Name{
Local: "Rights",
}
parentStart := xml.StartElement{
Name: parentName,
}
if err := e.EncodeToken(parentStart); err != nil {
return err
}
for _, right := range r.Rights {
name := NameElement{
Value: right.Name,
}
value := AccessElement{
Value: strconv.Itoa(right.Access),
}
if err := e.Encode(&name); err != nil {
return err
}
if err := e.Encode(&value); err != nil {
return err
}
}
parentEnd := xml.EndElement{
Name: parentName,
}
if err := e.EncodeToken(parentEnd); err != nil {
return err
}
return nil
}
type right struct {
Name string
Access int
}
func main() {
var result sessionInfo
if err := xml.Unmarshal(data, &result); err != nil {
log.Fatalln(err)
}
if out, err := xml.MarshalIndent(result, "", " "); err != nil {
log.Fatalln(err)
} else {
fmt.Println(string(out))
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论