将XML元素名称解组为不同的属性

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

Unmarshal XML element name to different property

问题

我目前正在为NameSilo API编写一个库。我在getPriceList API上遇到了问题,它返回以下格式的XML:

<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>

如你所见,每个顶级域名都有一个元素。我想将元素名称(例如:com、net)解组为一个名为TLD的属性(而不是XMLName)。

阅读https://golang.org/src/encoding/xml/marshal.go的34-39行后,似乎这是不可能的。

我尝试了以下代码,但它不起作用。

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"`
}

我是否有办法实现这一点,或者说除了XMLName之外,无法为xml元素名称指定其他属性名称。

更新:我进一步查看了代码,并找到了this,这让我觉得我不能轻易地做到这一点。

英文:

I am currently writing a library for the NameSilo API. I am stuck on the getPriceList api, which returns XML like this:

&lt;namesilo&gt;
    &lt;request&gt;
        &lt;operation&gt;getPrices&lt;/operation&gt;
        &lt;ip&gt;55.555.55.55&lt;/ip&gt;
    &lt;/request&gt;
    &lt;reply&gt;
        &lt;code&gt;300&lt;/code&gt;
        &lt;detail&gt;success&lt;/detail&gt;
        &lt;com&gt;
            &lt;registration&gt;8.99&lt;/registration&gt;
            &lt;transfer&gt;8.39&lt;/transfer&gt;
            &lt;renew&gt;8.99&lt;/renew&gt;
        &lt;/com&gt;
        &lt;net&gt;
            &lt;registration&gt;9.29&lt;/registration&gt;
            &lt;transfer&gt;8.99&lt;/transfer&gt;
            &lt;renew&gt;9.29&lt;/renew&gt;
        &lt;/net&gt;
    &lt;/reply&gt;
&lt;/namesilo&gt;

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:&quot;operation&quot;`
		IP        string `xml:&quot;ip&quot;`
	} `xml:&quot;request&quot;`
}

type GetPricesResponse struct {
	APIResponse
	Reply []struct {
		Domains []struct {
			TLD xml.name
			Registration string `xml:&quot;registration&quot;`
			Transfer     string `xml:&quot;transfer&quot;`
			Renew        string `xml:&quot;renew&quot;`
		} `xml:&quot;,any&quot;`
	} `xml:&quot;reply&quot;`
}

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.

答案1

得分: 2

你不需要将数据解组为最终类型。你可以将数据解组为一组与该数据结构相对应的类型,然后将其转换为你偏好的表示形式,以供其他地方使用(例如,为了快速查找,你可能希望有一个将域名映射到价格记录的map[string]PriceRecord)。我认为应该这样考虑,而不是试图一步完成翻译,这样你就完全依赖于他们选择生成的XML格式——如果它发生变化,你的数据结构也必须相应变化。

英文:

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.

答案2

得分: 2

XMLName xml.Name没有更简单的替代方法。

可以通过满足unmarshaller接口的类型来实现你想要的功能。增加的复杂性可能不值得。Playground示例

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\n", 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>`)
英文:

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 (
&quot;encoding/xml&quot;
&quot;fmt&quot;
&quot;log&quot;
)
func main() {
var data Data
if err := xml.Unmarshal(payload, &amp;data); err != nil {
log.Fatal(err)
}
for k, v := range data.Reply.Domains {
fmt.Printf(&quot;%d: %#v\n&quot;, k, v)
}
}
type Domain struct {
TLD          string
Registration string `xml:&quot;registration&quot;`
Transfer     string `xml:&quot;transfer&quot;`
Renew        string `xml:&quot;renew&quot;`
}
func (domain *Domain) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
v := struct {
XMLName      xml.Name
Registration string `xml:&quot;registration&quot;`
Transfer     string `xml:&quot;transfer&quot;`
Renew        string `xml:&quot;renew&quot;`
}{}
d.DecodeElement(&amp;v, &amp;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:&quot;operation&quot;`
Ip        string `xml:&quot;ip&quot;`
} `xml:&quot;request&quot;`
Reply struct {
Code    string   `xml:&quot;code&quot;`
Detail  string   `xml:&quot;detail&quot;`
Domains []Domain `xml:&quot;,any&quot;`
} `xml:&quot;reply&quot;`
}
var payload = []byte(`&lt;namesilo&gt;
&lt;request&gt;
&lt;operation&gt;getPrices&lt;/operation&gt;
&lt;ip&gt;55.555.55.55&lt;/ip&gt;
&lt;/request&gt;
&lt;reply&gt;
&lt;code&gt;300&lt;/code&gt;
&lt;detail&gt;success&lt;/detail&gt;
&lt;com&gt;
&lt;registration&gt;8.99&lt;/registration&gt;
&lt;transfer&gt;8.39&lt;/transfer&gt;
&lt;renew&gt;8.99&lt;/renew&gt;
&lt;/com&gt;
&lt;net&gt;
&lt;registration&gt;9.29&lt;/registration&gt;
&lt;transfer&gt;8.99&lt;/transfer&gt;
&lt;renew&gt;9.29&lt;/renew&gt;
&lt;/net&gt;
&lt;/reply&gt;
&lt;/namesilo&gt;`)

huangapple
  • 本文由 发表于 2017年3月20日 07:44:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/42893752.html
匿名

发表评论

匿名网友

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

确定