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 树莓派与pix飞控通信
  • ¥15 自动转发微信群信息到另外一个微信群
  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题