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
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 (
"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>`
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:
<whatever>
<HostSystemIdentificationInfo .../>
<HostSystemIdentificationInfo .../>
<HostSystemIdentificationInfo .../>
</whatever>
type HostSystemIdentificationInfo struct {
IdentifierValue string
// ...
}
type whatever struct {
Info []HostSystemIdentificationInfo `xml:"HostSystemIdentificationInfo"`
}
(if necessary, wrap the XML in a fake top-level element).
See the later comments on Golang marshal dynamic xml element name.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With