如何将没有 root/parent 元素的 XML 转换为结构?
How to convert XML without root/parent element to struct?
API 来自远程服务器的响应(响应内容类型:text/html):
<status>success</status>
<statusmsg>online</statusmsg>
<vmstat>online</vmstat>
<hostname>kvm-vps2</hostname>
<ipaddress>123.456.789.99</ipaddress>
我尝试将上面的 API 响应解析为 Go 结构。 None 以下方法有效:
方法 #1 代码
type result struct {
Status string `xml:",chardata"`
Statusmsg string `xml:",chardata"`
Vmstat string `xml:",chardata"`
Hostname string `xml:",chardata"`
Ipaddress string `xml:",chardata"`
}
方法 #2 代码
type result struct {
Status string `xml:"status"`
Statusmsg string `xml:"statusmsg"`
Vmstat string `xml:"vmstat"`
Hostname string `xml:"hostname"`
Ipaddress string `xml:"ipaddress"`
}
从方法 1 中,我只设法获得了 Status
值。其他值为空。对于方法 #2,所有值都是空的。
解组代码:
// other code
r := result{}
err = xml.Unmarshal(body, &r)
// other code
这里应该修复什么,以便我可以访问所有 API 响应值?
该响应不是有效的 XML 文档,因此您无法按原样一步解码或不进行修改/"decoration".
以下解决方案使用 body
作为未读响应正文流:Response.Body
.
1。解码为一系列 XML 文档
但是,它可以看作是 XML 个文档的 系列 ,因此您可以使用 xml.Decoder
逐个解码它们一.
例如:
var res result
dec := xml.NewDecoder(body)
var err error
decField := func(v interface{}) {
if err == nil {
err = dec.Decode(v)
}
}
decField(&res.Status)
decField(&res.Statusmsg)
decField(&res.Vmstat)
decField(&res.Hostname)
decField(&res.Ipaddress)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", res)
此输出(在 Go Playground 上尝试):
{Status:success Statusmsg:online Vmstat:online Hostname:kvm-vps2 Ipaddress:123.456.789.99}
是的,逐场解码不方便
2。读取正文并用根标签包装
另一种选择是用标签包装它,使其成为有效的 XML 文档:
buf := &bytes.Buffer{}
buf.WriteString("<root>")
if _, err := io.Copy(buf, body); err != nil {
panic(err)
}
buf.WriteString("</root>")
var res result
if err := xml.Unmarshal(buf.Bytes(), &res); err != nil {
panic(err)
}
fmt.Printf("%+v\n", res)
这输出相同。在 Go Playground.
上试用
是的,上述解决方案必须先将正文读入内存。
3。包装主体流而不将其读入内存
虽然我们可以避免读取并将正文保存在内存中。我们可以构造一个 reader ,在阅读时首先提供开始包装标签,然后是 "original" 主体,最后是结束标签。为此,我们可以使用 io.MultiReader()
:
r := io.MultiReader(
strings.NewReader("<root>"),
body,
strings.NewReader("</root>"),
)
var res result
if err := xml.NewDecoder(r).Decode(&res); err != nil {
panic(err)
}
fmt.Printf("%+v\n", res)
这再次输出相同的结果。在 Go Playground.
上试试这个
API 来自远程服务器的响应(响应内容类型:text/html):
<status>success</status>
<statusmsg>online</statusmsg>
<vmstat>online</vmstat>
<hostname>kvm-vps2</hostname>
<ipaddress>123.456.789.99</ipaddress>
我尝试将上面的 API 响应解析为 Go 结构。 None 以下方法有效:
方法 #1 代码
type result struct {
Status string `xml:",chardata"`
Statusmsg string `xml:",chardata"`
Vmstat string `xml:",chardata"`
Hostname string `xml:",chardata"`
Ipaddress string `xml:",chardata"`
}
方法 #2 代码
type result struct {
Status string `xml:"status"`
Statusmsg string `xml:"statusmsg"`
Vmstat string `xml:"vmstat"`
Hostname string `xml:"hostname"`
Ipaddress string `xml:"ipaddress"`
}
从方法 1 中,我只设法获得了 Status
值。其他值为空。对于方法 #2,所有值都是空的。
解组代码:
// other code
r := result{}
err = xml.Unmarshal(body, &r)
// other code
这里应该修复什么,以便我可以访问所有 API 响应值?
该响应不是有效的 XML 文档,因此您无法按原样一步解码或不进行修改/"decoration".
以下解决方案使用 body
作为未读响应正文流:Response.Body
.
1。解码为一系列 XML 文档
但是,它可以看作是 XML 个文档的 系列 ,因此您可以使用 xml.Decoder
逐个解码它们一.
例如:
var res result
dec := xml.NewDecoder(body)
var err error
decField := func(v interface{}) {
if err == nil {
err = dec.Decode(v)
}
}
decField(&res.Status)
decField(&res.Statusmsg)
decField(&res.Vmstat)
decField(&res.Hostname)
decField(&res.Ipaddress)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", res)
此输出(在 Go Playground 上尝试):
{Status:success Statusmsg:online Vmstat:online Hostname:kvm-vps2 Ipaddress:123.456.789.99}
是的,逐场解码不方便
2。读取正文并用根标签包装
另一种选择是用标签包装它,使其成为有效的 XML 文档:
buf := &bytes.Buffer{}
buf.WriteString("<root>")
if _, err := io.Copy(buf, body); err != nil {
panic(err)
}
buf.WriteString("</root>")
var res result
if err := xml.Unmarshal(buf.Bytes(), &res); err != nil {
panic(err)
}
fmt.Printf("%+v\n", res)
这输出相同。在 Go Playground.
上试用是的,上述解决方案必须先将正文读入内存。
3。包装主体流而不将其读入内存
虽然我们可以避免读取并将正文保存在内存中。我们可以构造一个 reader ,在阅读时首先提供开始包装标签,然后是 "original" 主体,最后是结束标签。为此,我们可以使用 io.MultiReader()
:
r := io.MultiReader(
strings.NewReader("<root>"),
body,
strings.NewReader("</root>"),
)
var res result
if err := xml.NewDecoder(r).Decode(&res); err != nil {
panic(err)
}
fmt.Printf("%+v\n", res)
这再次输出相同的结果。在 Go Playground.
上试试这个