如何将没有 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.

上试试这个