解析 XML 中的重复字段

huangapple go评论137阅读模式
英文:

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 &lt;Rights&gt;&lt;/Rights&gt; block. It has several &lt;Name&gt;&lt;/Name&gt; and &lt;Access&gt;&lt;/Access&gt; fields.

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;SessionInfo&gt;
   &lt;SID&gt;000000000000&lt;/SID&gt;
   &lt;Challenge&gt;1337&lt;/Challenge&gt;
   &lt;BlockTime&gt;0&lt;/BlockTime&gt;
   &lt;Rights&gt;
      &lt;Name&gt;Dial&lt;/Name&gt;
      &lt;Access&gt;2&lt;/Access&gt;
      &lt;Name&gt;App&lt;/Name&gt;
      &lt;Access&gt;2&lt;/Access&gt;
      &lt;Name&gt;HomeAuto&lt;/Name&gt;
      &lt;Access&gt;2&lt;/Access&gt;
      &lt;Name&gt;BoxAdmin&lt;/Name&gt;
      &lt;Access&gt;2&lt;/Access&gt;
      &lt;Name&gt;Phone&lt;/Name&gt;
      &lt;Access&gt;2&lt;/Access&gt;
      &lt;Name&gt;NAS&lt;/Name&gt;
      &lt;Access&gt;2&lt;/Access&gt;
   &lt;/Rights&gt;
&lt;/SessionInfo&gt;

I would like to have this converted into a rights struct.

type sessionInfo struct {
	XMLName    xml.Name `xml:&quot;SessionInfo&quot;`
	SID        string   `xml:&quot;SID&quot;`
	Challenge  string   `xml:&quot;Challenge&quot;`
	BlockTime  uint     `xml:&quot;BlockTime&quot;`
	Rights     []rights `xml:&quot;Rights&quot;`
}

type rights struct {
	Name   string `xml:&quot;Name&quot;`
	Access int    `xml:&quot;Access&quot;`
}

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?

&lt;SessionInfo&gt;
    &lt;SID&gt;000000000000&lt;/SID&gt;
    &lt;Challenge&gt;1337&lt;/Challenge&gt;
    &lt;BlockTime&gt;0&lt;/BlockTime&gt;
    &lt;Rights&gt;
        &lt;Name&gt;NAS&lt;/Name&gt;
        &lt;Access&gt;2&lt;/Access&gt;
    &lt;/Rights&gt;
&lt;/SessionInfo&gt;

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 (
&quot;encoding/xml&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;
&quot;strconv&quot;
)
var data = []byte(`&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;SessionInfo&gt;
&lt;SID&gt;000000000000&lt;/SID&gt;
&lt;Challenge&gt;1337&lt;/Challenge&gt;
&lt;BlockTime&gt;0&lt;/BlockTime&gt;
&lt;Rights&gt;
&lt;Name&gt;Dial&lt;/Name&gt;
&lt;Access&gt;2&lt;/Access&gt;
&lt;Name&gt;App&lt;/Name&gt;
&lt;Access&gt;2&lt;/Access&gt;
&lt;Name&gt;HomeAuto&lt;/Name&gt;
&lt;Access&gt;2&lt;/Access&gt;
&lt;Name&gt;BoxAdmin&lt;/Name&gt;
&lt;Access&gt;2&lt;/Access&gt;
&lt;Name&gt;Phone&lt;/Name&gt;
&lt;Access&gt;2&lt;/Access&gt;
&lt;Name&gt;NAS&lt;/Name&gt;
&lt;Access&gt;2&lt;/Access&gt;
&lt;/Rights&gt;
&lt;/SessionInfo&gt;`)
type sessionInfo struct {
XMLName   xml.Name `xml:&quot;SessionInfo&quot;`
SID       string   `xml:&quot;SID&quot;`
Challenge string   `xml:&quot;Challenge&quot;`
BlockTime uint     `xml:&quot;BlockTime&quot;`
Rights    *rights  `xml:&quot;Rights&quot;`
}
type rights struct {
Rights []*right
}
type NameElement struct {
XMLName xml.Name `xml:&quot;Name&quot;`
Value   string   `xml:&quot;,chardata&quot;`
}
type AccessElement struct {
XMLName xml.Name `xml:&quot;Access&quot;`
Value   string   `xml:&quot;,chardata&quot;`
}
func (r *rights) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
var name NameElement
var access AccessElement
if err := d.Decode(&amp;name); err != nil {
if err == io.EOF {
break
}
return err
}
if err := d.Decode(&amp;access); err != nil {
return err
}
value, err := strconv.Atoi(access.Value)
if err != nil {
return err
}
r.Rights = append(r.Rights, &amp;right{
Name:   name.Value,
Access: value,
})
}
return nil
}
func (r *rights) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
parentName := xml.Name{
Local: &quot;Rights&quot;,
}
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(&amp;name); err != nil {
return err
}
if err := e.Encode(&amp;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, &amp;result); err != nil {
log.Fatalln(err)
}
if out, err := xml.MarshalIndent(result, &quot;&quot;, &quot;  &quot;); err != nil {
log.Fatalln(err)
} else {
fmt.Println(string(out))
}
}

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

huangapple
  • 本文由 发表于 2016年11月13日 22:50:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/40575126.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定