douan7601 2016-11-13 14:50
浏览 268
已采纳

解析xml中的重复字段

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条回答 默认 最新

  • douju2012 2016-11-13 15:24
    关注

    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))
        }
    }
    

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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 请问为什么我配置IPsec后PC1 ping不通 PC2,抓包出来数据包也并没有被加密
  • ¥200 求博主教我搞定neo4j简易问答系统,有偿
  • ¥15 nginx的使用与作用
  • ¥100 关于#VijeoCitect#的问题,如何解决?(标签-ar|关键词-数据类型)
  • ¥15 一个矿井排水监控系统的plc梯形图,求各程序段都是什么意思
  • ¥50 安卓10如何在没有root权限的情况下设置开机自动启动指定app?
  • ¥15 ats2837 spi2从机的代码
  • ¥200 wsl2 vllm qwen1.5部署问题
  • ¥100 有偿求数字经济对经贸的影响机制的一个数学模型,弄不出来已经快要碎掉了
  • ¥15 数学建模数学建模需要