解组嵌套的 XML(使用 Golang)

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

Unmarshaling nested xml golang

问题

我想将一个嵌套的 XML 转换为 Golang 结构体。我想要转换的 XML 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<status>
	<authorized>true</authorized>
	<plan>Basic</plan>
	<usage_reports>
		<usage_report metric="hits" period="day">
			<period_start>2021-07-20 00:00:00 +0000</period_start>
			<period_end>2021-07-21 00:00:00 +0000</period_end>
			<max_value>1000000</max_value>
			<current_value>0</current_value>
		</usage_report>
		<usage_report metric="hits.82343" period="day">
			<period_start>2021-07-20 00:00:00 +0000</period_start>
			<period_end>2021-07-21 00:00:00 +0000</period_end>
			<max_value>1000000</max_value>
			<current_value>0</current_value>
		</usage_report>
	</usage_reports>
</status>

为了进行转换,我定义了两个 Golang 结构体,如下所示:

// UsageReport 表示特定指标的使用报告。
type UsageReport struct {
	Usage   string `xml:"usage_report,chardata"`
	Metric  string `xml:"metric,attr"`
	Period  string `xml:"period,attr"`
	Start   string `xml:"period_start"`
	End     string `xml:"period_end"`
	Max     int64  `xml:"max_value"`
	Current int64  `xml:"current_value"`
}

// AuthResponse 表示授权调用的响应结构。
type AuthResponse struct {
	Status     xml.Name      `xml:"status"`
	Authorized bool          `xml:"authorized"`
	Plan       string        `xml:"plan"`
	Usages     []UsageReport `xml:"usage_reports>usage_report"`
}

但是当我尝试使用 xml.Unmarshal 进行转换时,只有 PlanAuthorized 的值被转换。转换的结果如下所示:

XML: {{ } true Basic []}
英文:

I want to unmarshall a nested xml to a golang struct. The xml that I want to unmarshall is shown below.

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;status&gt;
	&lt;authorized&gt;true&lt;/authorized&gt;
	&lt;plan&gt;Basic&lt;/plan&gt;
	&lt;usage_reports&gt;
		&lt;usage_report metric=&quot;hits&quot; period=&quot;day&quot;&gt;
			&lt;period_start&gt;2021-07-20 00:00:00 +0000&lt;/period_start&gt;
			&lt;period_end&gt;2021-07-21 00:00:00 +0000&lt;/period_end&gt;
			&lt;max_value&gt;1000000&lt;/max_value&gt;
			&lt;current_value&gt;0&lt;/current_value&gt;
		&lt;/usage_report&gt;
		&lt;usage_report metric=&quot;hits.82343&quot; period=&quot;day&quot;&gt;
			&lt;period_start&gt;2021-07-20 00:00:00 +0000&lt;/period_start&gt;
			&lt;period_end&gt;2021-07-21 00:00:00 +0000&lt;/period_end&gt;
			&lt;max_value&gt;1000000&lt;/max_value&gt;
			&lt;current_value&gt;0&lt;/current_value&gt;
		&lt;/usage_report&gt;
	&lt;/usage_reports&gt;
&lt;/status&gt;

To unmarshall I have defined two go structs as follows.

// UsageReport represents the usage report of a particular metric.
type UsageReport struct {
	Usage   string `xml:&quot;usage_report,chardata&quot;`
	Metric  string `xml:&quot;metric,attr&quot;`
	Period  string `xml:&quot;period,attr&quot;`
	Start   string `xml:&quot;period_start&quot;`
	End     string `xml:&quot;period_end&quot;`
	Max     int64  `xml:&quot;max_value&quot;`
	Current int64  `xml:&quot;current_value&quot;`
}

// AuthResponse represents the structure of the response from authorize calls.
type AuthResponse struct {
	Status     xml.Name      `xml:&quot;status&quot;`
	Authorized bool          `xml:&quot;authorized&quot;`
	Plan       string        `xml:&quot;plan&quot;`
	Usages     []UsageReport `xml:&quot;usage_reports&quot;`
}

But when I try to unmarshall using xml.Unmarshal, only Plan and Authorized values gets unmarshalled. The result of unmarshalling is shown below.

XML: {{ } true Basic []}

答案1

得分: 2

请尝试使用这个Go Playground。我使用这个工具来帮助我从XML数据生成Go类型。

package main

import (
	"encoding/xml"
	"fmt"
)

type Status struct {
	XMLName      xml.Name `xml:"status"`
	Text         string   `xml:",chardata"`
	Authorized   string   `xml:"authorized"`
	Plan         string   `xml:"plan"`
	UsageReports struct {
		Text        string `xml:",chardata"`
		UsageReport []struct {
			Text         string `xml:",chardata"`
			Metric       string `xml:"metric,attr"`
			Period       string `xml:"period,attr"`
			PeriodStart  string `xml:"period_start"`
			PeriodEnd    string `xml:"period_end"`
			MaxValue     string `xml:"max_value"`
			CurrentValue string `xml:"current_value"`
		} `xml:"usage_report"`
	} `xml:"usage_reports"`
}

func main() {
	status := Status{}
	err := xml.Unmarshal([]byte(xmlStr), &status)
	if err != nil {
		panic(err)
	}
	fmt.Println(status.UsageReports.UsageReport[0].MaxValue)
}

const xmlStr = `<?xml version="1.0" encoding="UTF-8"?><status><authorized>true</authorized><plan>Basic</plan><usage_reports><usage_report metric="hits" period="day"><period_start>2021-07-20 00:00:00 +0000</period_start><period_end>2021-07-21 00:00:00 +0000</period_end><max_value>1000000</max_value><current_value>0</current_value></usage_report><usage_report metric="hits.82343" period="day"><period_start>2021-07-20 00:00:00 +0000</period_start><period_end>2021-07-21 00:00:00 +0000</period_end><max_value>1000000</max_value><current_value>0</current_value></usage_report></usage_reports></status>`
英文:

Try this go playground. I use this tool to cheat and help me generate Go types from XML data.

package main

import (
	&quot;encoding/xml&quot;
	&quot;fmt&quot;
)

type Status struct {
	XMLName      xml.Name `xml:&quot;status&quot;`
	Text         string   `xml:&quot;,chardata&quot;`
	Authorized   string   `xml:&quot;authorized&quot;`
	Plan         string   `xml:&quot;plan&quot;`
	UsageReports struct {
		Text        string `xml:&quot;,chardata&quot;`
		UsageReport []struct {
			Text         string `xml:&quot;,chardata&quot;`
			Metric       string `xml:&quot;metric,attr&quot;`
			Period       string `xml:&quot;period,attr&quot;`
			PeriodStart  string `xml:&quot;period_start&quot;`
			PeriodEnd    string `xml:&quot;period_end&quot;`
			MaxValue     string `xml:&quot;max_value&quot;`
			CurrentValue string `xml:&quot;current_value&quot;`
		} `xml:&quot;usage_report&quot;`
	} `xml:&quot;usage_reports&quot;`
}

func main() {
	status := Status{}
	err := xml.Unmarshal([]byte(xmlStr), &amp;status)
	if err != nil {
		panic(err)
	}
	fmt.Println(status.UsageReports.UsageReport[0].MaxValue)
}

const xmlStr = `&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;status&gt;&lt;authorized&gt;true&lt;/authorized&gt;&lt;plan&gt;Basic&lt;/plan&gt;&lt;usage_reports&gt;&lt;usage_report metric=&quot;hits&quot; period=&quot;day&quot;&gt;&lt;period_start&gt;2021-07-20 00:00:00 +0000&lt;/period_start&gt;&lt;period_end&gt;2021-07-21 00:00:00 +0000&lt;/period_end&gt;&lt;max_value&gt;1000000&lt;/max_value&gt;&lt;current_value&gt;0&lt;/current_value&gt;&lt;/usage_report&gt;&lt;usage_report metric=&quot;hits.82343&quot; period=&quot;day&quot;&gt;&lt;period_start&gt;2021-07-20 00:00:00 +0000&lt;/period_start&gt;&lt;period_end&gt;2021-07-21 00:00:00 +0000&lt;/period_end&gt;&lt;max_value&gt;1000000&lt;/max_value&gt;&lt;current_value&gt;0&lt;/current_value&gt;&lt;/usage_report&gt;&lt;/usage_reports&gt;&lt;/status&gt;`

答案2

得分: 1

为了获取Usages字段的值,请根据mkopriva的建议将xml:"usage_reports"修改为xml:"usage_reports>usage_report"

package main

import (
	"encoding/xml"
	"fmt"
)

type UsageReport struct {
	Metric  string `xml:"metric,attr"`
	Period  string `xml:"period,attr"`
	Start   string `xml:"period_start"`
	End     string `xml:"period_end"`
	Max     int64  `xml:"max_value"`
	Current int64  `xml:"current_value"`
}

type AuthResponse struct {
	Status     xml.Name      `xml:"status"`
	Authorized bool          `xml:"authorized"`
	Plan       string        `xml:"plan"`
	Usages     []UsageReport `xml:"usage_reports>usage_report"`
}

var data = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<status>
    <authorized>true</authorized>
    <plan>Basic</plan>
    <usage_reports>
        <usage_report metric="hits" period="day">
            <period_start>2021-07-20 00:00:00 +0000</period_start>
            <period_end>2021-07-21 00:00:00 +0000</period_end>
            <max_value>1000000</max_value>
            <current_value>0</current_value>
        </usage_report>
        <usage_report metric="hits.82343" period="day">
            <period_start>2021-07-20 00:00:00 +0000</period_start>
            <period_end>2021-07-21 00:00:00 +0000</period_end>
            <max_value>1000000</max_value>
            <current_value>0</current_value>
        </usage_report>
    </usage_reports>
</status>
`)

func main() {
	r := AuthResponse{}
	if err := xml.Unmarshal(data, &r); err != nil {
		panic(err)
	}
	fmt.Println(r)
}

输出结果:

{{ } true Basic [{hits day 2021-07-20 00:00:00 +0000 2021-07-21 00:00:00 +0000 1000000 0} {hits.82343 day 2021-07-20 00:00:00 +0000 2021-07-21 00:00:00 +0000 1000000 0}]}
英文:

In order to get the value of Usages field modify xml:&quot;usage_reports&quot; to xml:&quot;usage_reports&gt;usage_report&quot; as per the suggestions of mkopriva.

package main

import (
	&quot;encoding/xml&quot;
	&quot;fmt&quot;
)

type UsageReport struct {
	Metric  string `xml:&quot;metric,attr&quot;`
	Period  string `xml:&quot;period,attr&quot;`
	Start   string `xml:&quot;period_start&quot;`
	End     string `xml:&quot;period_end&quot;`
	Max     int64  `xml:&quot;max_value&quot;`
	Current int64  `xml:&quot;current_value&quot;`
}

type AuthResponse struct {
	Status     xml.Name      `xml:&quot;status&quot;`
	Authorized bool          `xml:&quot;authorized&quot;`
	Plan       string        `xml:&quot;plan&quot;`
	Usages     []UsageReport `xml:&quot;usage_reports&gt;usage_report&quot;`
}

var data = []byte(`&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;status&gt;
    &lt;authorized&gt;true&lt;/authorized&gt;
    &lt;plan&gt;Basic&lt;/plan&gt;
    &lt;usage_reports&gt;
        &lt;usage_report metric=&quot;hits&quot; period=&quot;day&quot;&gt;
            &lt;period_start&gt;2021-07-20 00:00:00 +0000&lt;/period_start&gt;
            &lt;period_end&gt;2021-07-21 00:00:00 +0000&lt;/period_end&gt;
            &lt;max_value&gt;1000000&lt;/max_value&gt;
            &lt;current_value&gt;0&lt;/current_value&gt;
        &lt;/usage_report&gt;
        &lt;usage_report metric=&quot;hits.82343&quot; period=&quot;day&quot;&gt;
            &lt;period_start&gt;2021-07-20 00:00:00 +0000&lt;/period_start&gt;
            &lt;period_end&gt;2021-07-21 00:00:00 +0000&lt;/period_end&gt;
            &lt;max_value&gt;1000000&lt;/max_value&gt;
            &lt;current_value&gt;0&lt;/current_value&gt;
        &lt;/usage_report&gt;
    &lt;/usage_reports&gt;
&lt;/status&gt;
`)

func main() {
	r := AuthResponse{}
	if err := xml.Unmarshal(data, &amp;r); err != nil {
		panic(err)
	}
	fmt.Println(r)
}

Output:

{{ } true Basic [{hits day 2021-07-20 00:00:00 +0000 2021-07-21 00:00:00 +0000 1000000 0} {hits.82343 day 2021-07-20 00:00:00 +0000 2021-07-21 00:00:00 +0000 1000000 0}]}

huangapple
  • 本文由 发表于 2021年7月20日 22:14:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/68456437.html
匿名

发表评论

匿名网友

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

确定