Reputation: 23
I'm having trouble accessing an attribute in a namespaced tag when using umarshal on some XML data. A working example of what I'm trying to accomplish is on line 14 of my code which demonstrates successfully loading the attribute name
from the <cpe-item>
tag, into the Name
field of the CPE
struct.
However, doing the same thing with a name-spaced tag such as on line 19 (loading the name
attribute from the <cpe23: cpe23-item>
tag into the Name
field of the CPE23
struct) does not work - no value is found.
I'm failing to see the discrepancy between these two actions and why one fails while the other doesn't.
package main
import (
"encoding/xml"
"fmt"
)
type CPEs struct {
XMLName xml.Name `xml:"cpe-list"`
CPEs []CPE `xml:"cpe-item"`
}
type CPE struct {
Name string `xml:"name,attr"`
CPE23 CPE23 `xml:"cpe23: cpe23-item"`
}
type CPE23 struct {
Name string `xml:"cpe23: cpe23-item,name,attr"`
}
func main() {
var cpes CPEs
contents := `
<cpe-list xmlns:meta="http://scap.nist.gov/schema/cpe-dictionary-metadata/0.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cpe-23="http://scap.nist.gov/schema/cpe-extension/2.3" xmlns="http://cpe.mitre.org/dictionary/2.0" xmlns:config="http://scap.nist.gov/schema/configuration/0.1" xmlns:ns6="http://scap.nist.gov/schema/scap-core/0.1" xmlns:scap-core="http://scap.nist.gov/schema/scap-core/0.3" xsi:schemaLocation="http://scap.nist.gov/schema/cpe-extension/2.3 https://scap.nist.gov/schema/cpe/2.3/cpe-dictionary-extension_2.3.xsd http://cpe.mitre.org/dictionary/2.0 https://scap.nist.gov/schema/cpe/2.3/cpe-dictionary_2.3.xsd http://scap.nist.gov/schema/cpe-dictionary-metadata/0.2 https://scap.nist.gov/schema/cpe/2.1/cpe-dictionary-metadata_0.2.xsd http://scap.nist.gov/schema/scap-core/0.3 https://scap.nist.gov/schema/nvd/scap-core_0.3.xsd http://scap.nist.gov/schema/configuration/0.1 https://scap.nist.gov/schema/nvd/configuration_0.1.xsd http://scap.nist.gov/schema/scap-core/0.1 https://scap.nist.gov/schema/nvd/scap-core_0.1.xsd">
<cpe-item name="I'm the cpe name!"><!--I can parse this attribute-->
<title>I'm the title!</title>
<references>
<reference href="https://example.com">Example</reference>
<reference href="https://example2.com">Example 2</reference>
</references>
<cpe-23:cpe23-item name="CPE 2.3 name!"/><!--I can't parse this attribute-->
</cpe-item>
</cpe-list>`
xml.Unmarshal([]byte(contents), &cpes)
for i := 0; i < len(cpes.CPEs); i++ {
fmt.Println("CPE Name: " + cpes.CPEs[i].Name)
fmt.Println("CPE23 Name: " + cpes.CPEs[i].CPE23.Name)
}
}
Go playground link https://play.golang.org/p/eRMrFePDM4K
Upvotes: 2
Views: 134
Reputation: 45041
Unfortunately the documentation doesn't provide much insight. Compare with this relatively recent question.
However, it is possible to make your code work by simply removing the namespace from the tag of the CPE23
field:
type CPE struct {
Name string `xml:"name,attr"`
CPE23 CPE23 `xml:"cpe23-item"`
}
type CPE23 struct {
Name string `xml:"name,attr"`
}
...or by prefixing the tag name with the full namespace, separated by a whitespace:
type CPE struct {
Name string `xml:"name,attr"`
CPE23 CPE23 `xml:"http://scap.nist.gov/schema/cpe-extension/2.3 cpe23-item"`
}
See this Go Play
By looking at the source code of the encoding/xml
package, it's possible to see that the tokenizer reads an xml element tag in the form of <ns:foo>
, where xmlns:ns="http://mynamespace.com"
, into an xml.Name
struct in the form:
xml.Name{
Space: "http://mynamespace.com",
Local: "foo"
}
And then it resolves tags on your destination struct based on those two fields. If there's no namespace on your struct tag, it matches on the value of Local
only.
Upvotes: 1