将XML元素名称解组为其他属性
I am currently writing a library for the NameSilo API. I am stuck on the getPriceList api, which returns XML like this:
<namesilo>
<request>
<operation>getPrices</operation>
<ip>55.555.55.55</ip>
</request>
<reply>
<code>300</code>
<detail>success</detail>
<com>
<registration>8.99</registration>
<transfer>8.39</transfer>
<renew>8.99</renew>
</com>
<net>
<registration>9.29</registration>
<transfer>8.99</transfer>
<renew>9.29</renew>
</net>
</reply>
</namesilo>
As you can see, there is an element for each TLD. I would like to unmarshal the element name (example: com, net) into a property which is not called XMLName
(I want it to be called TLD).
After reading lines 34-39 of https://golang.org/src/encoding/xml/marshal.go, it seems like this is not possible.
I have tried the following code, but it does not work.
type APIResponse struct {
Request struct {
Operation string `xml:"operation"`
IP string `xml:"ip"`
} `xml:"request"`
}
type GetPricesResponse struct {
APIResponse
Reply []struct {
Domains []struct {
TLD xml.name
Registration string `xml:"registration"`
Transfer string `xml:"transfer"`
Renew string `xml:"renew"`
} `xml:",any"`
} `xml:"reply"`
}
Is there any way I can do this, or is it not possible to have a property name other than XMLName
for the xml element name.
UPDATE: I looked a bit more into the code, and I found this, which makes me think I cannot do this easily.
There's no simpler alternative to XMLName xml.Name
.
It's possible to do what you want with a type satisfying the unmarshaller interface. The added complexity is probably not worthwhile. Playground example:
package main
import (
"encoding/xml"
"fmt"
"log"
)
func main() {
var data Data
if err := xml.Unmarshal(payload, &data); err != nil {
log.Fatal(err)
}
for k, v := range data.Reply.Domains {
fmt.Printf("%d: %#v
", k, v)
}
}
type Domain struct {
TLD string
Registration string `xml:"registration"`
Transfer string `xml:"transfer"`
Renew string `xml:"renew"`
}
func (domain *Domain) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
v := struct {
XMLName xml.Name
Registration string `xml:"registration"`
Transfer string `xml:"transfer"`
Renew string `xml:"renew"`
}{}
d.DecodeElement(&v, &start)
domain.TLD = v.XMLName.Local
domain.Registration = v.Registration
domain.Transfer = v.Transfer
domain.Renew = v.Renew
return nil
}
type Data struct {
Request struct {
Operation string `xml:"operation"`
Ip string `xml:"ip"`
} `xml:"request"`
Reply struct {
Code string `xml:"code"`
Detail string `xml:"detail"`
Domains []Domain `xml:",any"`
} `xml:"reply"`
}
var payload = []byte(`<namesilo>
<request>
<operation>getPrices</operation>
<ip>55.555.55.55</ip>
</request>
<reply>
<code>300</code>
<detail>success</detail>
<com>
<registration>8.99</registration>
<transfer>8.39</transfer>
<renew>8.99</renew>
</com>
<net>
<registration>9.29</registration>
<transfer>8.99</transfer>
<renew>9.29</renew>
</net>
</reply>
</namesilo>`)
You don't need to unmarshal into your final type. You could unmarshal into a separate set of types which mirror this data structure, then just translated into your preferred representation for use elsewhere (for example for fast lookup you might want to have a map[string]PriceRecord mapping domain names to Price records). I'd think of it that way rather than necessarily trying to do the translation in one step, which ties you completely to whatever xml they choose to produce - if it changes, your data structures would have to change too.