简体   繁体   中英

Parse repeating fields in 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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM