在Golang中解析XML数组:只获取第一个元素

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

Unmarshal XML Array in Golang: Only Getting The First Element

问题

代码中的问题是在对XML进行解析时,只能解析到一个HostSystemIdentificationInfo结构体,而不是整个数组。要解决这个问题,你需要对XML进行适当的修改。

HostSystemIdentificationInfo结构体的定义中,将标签xml:"HostSystemIdentificationInfo"添加到结构体的开头,以指示该结构体是XML中的一个元素。修改后的代码如下所示:

type HostSystemIdentificationInfo struct {
	XMLName        xml.Name `xml:"HostSystemIdentificationInfo"`
	IdentifierValue string   `xml:"identifierValue"`
	IdentifierType  struct {
		Label   string `xml:"label"`
		Summary string `xml:"summary"`
		Key     string `xml:"key"`
	} `xml:"identifierType"`
}

这样修改后,你应该能够正确解析整个HostSystemIdentificationInfo数组了。

英文:

Code:

type HostSystemIdentificationInfo []struct {
	IdentiferValue string `xml:"identifierValue"`
	IdentiferType  struct {
		Label   string `xml:"label"`
		Summary string `xml:"summary"`
		Key     string `xml:"key"`
	} `xml:"identifierType"`
}

func vsphereHost(v *vsphere.Vsphere, md *opentsdb.MultiDataPoint) error {
	res, err := v.Info("HostSystem", []string{
		"name",
		"summary.hardware.cpuMhz",
		"summary.hardware.memorySize", // bytes
		"summary.hardware.numCpuCores",
		"summary.hardware.numCpuCores",
		"summary.quickStats.overallCpuUsage",    // MHz
		"summary.quickStats.overallMemoryUsage", // MB
		"summary.hardware.otherIdentifyingInfo",
		"summary.hardware.model",
	})
	for _, r := range res {
		for _, p := range r.Props {
			if p.Name == "summary.hardware.otherIdentifyingInfo" {
				var t HostSystemIdentificationInfo
				fmt.Println(p.Val.Inner)
				err := xml.Unmarshal([]byte(p.Val.Inner), &t)
				if err != nil {
					return err
				}
				fmt.Println(t)
			}
		}
	}

Output:

<HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue> unknown</identifierValue><identifierType><label>Asset Tag</label><summary>Asset tag of the system</summary><key>AssetTag</key></identifierType></HostSystemIdentificationInfo><HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue>Dell System</identifierValue><identifierType><label>OEM specific string</label><summary>OEM specific string</summary><key>OemSpecificString</key></identifierType></HostSystemIdentificationInfo><HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue>5[0000]</identifierValue><identifierType><label>OEM specific string</label><summary>OEM specific string</summary><key>OemSpecificString</key></identifierType></HostSystemIdentificationInfo><HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue>REDACTED</identifierValue><identifierType><label>Service tag</label><summary>Service tag of the system</summary><key>ServiceTag</key></identifierType></HostSystemIdentificationInfo>
[{ unknown {Asset Tag Asset tag of the system AssetTag}}]
<HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue> unknown</identifierValue><identifierType><label>Asset Tag</label><summary>Asset tag of the system</summary><key>AssetTag</key></identifierType></HostSystemIdentificationInfo><HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue>Dell System</identifierValue><identifierType><label>OEM specific string</label><summary>OEM specific string</summary><key>OemSpecificString</key></identifierType></HostSystemIdentificationInfo><HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue>5[0000]</identifierValue><identifierType><label>OEM specific string</label><summary>OEM specific string</summary><key>OemSpecificString</key></identifierType></HostSystemIdentificationInfo><HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo"><identifierValue>REDCATED</identifierValue><identifierType><label>Service tag</label><summary>Service tag of the system</summary><key>ServiceTag</key></identifierType></HostSystemIdentificationInfo>
[{ unknown {Asset Tag Asset tag of the system AssetTag}}]

So the problem is when I unmarshal I'm only getting one of the HostSystemIdentification structs in the result instead of the full array. How do I fix this?

Here is a go playground with the problem reduced: http://play.golang.org/p/5uRJ6Eu8jK

答案1

得分: 6

由于您的字符串中有多个顶级实体,您需要创建一个xml.Decoder并多次调用其Decode方法。请参阅http://play.golang.org/p/_1a77YGLoX

package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"log"
)

type HostSystemIdentificationInfo []struct {
	IdentiferValue string `xml:"identifierValue"`
	IdentiferType  struct {
		Label   string `xml:"label"`
		Summary string `xml:"summary"`
		Key     string `xml:"key"`
	} `xml:"identifierType"`
}

func main() {
	d := xml.NewDecoder(bytes.NewBufferString(VV))
	for {
		var t HostSystemIdentificationInfo
		err := d.Decode(&t)
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(t)
	}
}

const VV = `<HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo">
  <identifierValue> unknown</identifierValue>
  <identifierType>
    <label>Asset Tag</label>
    <summary>Asset tag of the system</summary>
    <key>AssetTag</key>
  </identifierType>
</HostSystemIdentificationInfo>
<HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo">
  <identifierValue>Dell System</identifierValue>
  <identifierType>
    <label>OEM specific string</label>
    <summary>OEM specific string</summary>
    <key>OemSpecificString</key>
  </identifierType>
</HostSystemIdentificationInfo>
<HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo">
  <identifierValue>5[0000]</identifierValue>
  <identifierType>
    <label>OEM specific string</label>
    <summary>OEM specific string</summary>
    <key>OemSpecificString</key>
  </identifierType>
</HostSystemIdentificationInfo>
<HostSystemIdentificationInfo xsi:type="HostSystemIdentificationInfo">
  <identifierValue>REDACTED</identifierValue>
  <identifierType>
    <label>Service tag</label>
    <summary>Service tag of the system</summary>
    <key>ServiceTag</key>
  </identifierType>
</HostSystemIdentificationInfo>`
英文:

Since you have multiple top-level entities in the string, you must create a xml.Decoder and call its Decode method many times. See http://play.golang.org/p/_1a77YGLoX

package main
import (
&quot;bytes&quot;
&quot;encoding/xml&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;
)
type HostSystemIdentificationInfo []struct {
IdentiferValue string `xml:&quot;identifierValue&quot;`
IdentiferType  struct {
Label   string `xml:&quot;label&quot;`
Summary string `xml:&quot;summary&quot;`
Key     string `xml:&quot;key&quot;`
} `xml:&quot;identifierType&quot;`
}
func main() {
d := xml.NewDecoder(bytes.NewBufferString(VV))
for {
var t HostSystemIdentificationInfo
err := d.Decode(&amp;t)
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Println(t)
}
}
const VV = `&lt;HostSystemIdentificationInfo xsi:type=&quot;HostSystemIdentificationInfo&quot;&gt;
&lt;identifierValue&gt; unknown&lt;/identifierValue&gt;
&lt;identifierType&gt;
&lt;label&gt;Asset Tag&lt;/label&gt;
&lt;summary&gt;Asset tag of the system&lt;/summary&gt;
&lt;key&gt;AssetTag&lt;/key&gt;
&lt;/identifierType&gt;
&lt;/HostSystemIdentificationInfo&gt;
&lt;HostSystemIdentificationInfo xsi:type=&quot;HostSystemIdentificationInfo&quot;&gt;
&lt;identifierValue&gt;Dell System&lt;/identifierValue&gt;
&lt;identifierType&gt;
&lt;label&gt;OEM specific string&lt;/label&gt;
&lt;summary&gt;OEM specific string&lt;/summary&gt;
&lt;key&gt;OemSpecificString&lt;/key&gt;
&lt;/identifierType&gt;
&lt;/HostSystemIdentificationInfo&gt;
&lt;HostSystemIdentificationInfo xsi:type=&quot;HostSystemIdentificationInfo&quot;&gt;
&lt;identifierValue&gt;5[0000]&lt;/identifierValue&gt;
&lt;identifierType&gt;
&lt;label&gt;OEM specific string&lt;/label&gt;
&lt;summary&gt;OEM specific string&lt;/summary&gt;
&lt;key&gt;OemSpecificString&lt;/key&gt;
&lt;/identifierType&gt;
&lt;/HostSystemIdentificationInfo&gt;
&lt;HostSystemIdentificationInfo xsi:type=&quot;HostSystemIdentificationInfo&quot;&gt;
&lt;identifierValue&gt;REDACTED&lt;/identifierValue&gt;
&lt;identifierType&gt;
&lt;label&gt;Service tag&lt;/label&gt;
&lt;summary&gt;Service tag of the system&lt;/summary&gt;
&lt;key&gt;ServiceTag&lt;/key&gt;
&lt;/identifierType&gt;
&lt;/HostSystemIdentificationInfo&gt;`

答案2

得分: 2

XML解析器期望一个格式良好的XML文档,其中包含一个顶级元素。它读取第一个元素,假设它是整个文档,并在此处停止。

HostSystemIdentificationInfo元素的父元素开始解析:

<whatever>
<HostSystemIdentificationInfo .../>
<HostSystemIdentificationInfo .../>
<HostSystemIdentificationInfo .../>
</whatever>
type HostSystemIdentificationInfo struct {
IdentifierValue string 
// ...
}
type whatever struct {
Info []HostSystemIdentificationInfo `xml:"HostSystemIdentificationInfo"`
}

(如果需要,可以在一个虚假的顶级元素中包装XML)。

请参阅https://stackoverflow.com/questions/26867417/golang-marshal-dynamic-xml-element-name/26867656#26867656上的后续评论。

英文:

The XML parser expects a well-formed XML document with a single top-level element. It's reading the first element, assumes that is the whole document, and stops there.

Start from the parent element of the HostSystemIdentificationInfo elements and unmarshal that instead:

&lt;whatever&gt;
&lt;HostSystemIdentificationInfo .../&gt;
&lt;HostSystemIdentificationInfo .../&gt;
&lt;HostSystemIdentificationInfo .../&gt;
&lt;/whatever&gt;
type HostSystemIdentificationInfo struct {
IdentifierValue string 
// ...
}
type whatever struct {
Info []HostSystemIdentificationInfo `xml:&quot;HostSystemIdentificationInfo&quot;`
}

(if necessary, wrap the XML in a fake top-level element).

See the later comments on https://stackoverflow.com/questions/26867417/golang-marshal-dynamic-xml-element-name/26867656#26867656.

huangapple
  • 本文由 发表于 2014年12月19日 02:27:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/27553274.html
匿名

发表评论

匿名网友

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

确定