Go:嵌套结构中的提升字段用于 XML 解码

3

我有一个如下的XML转换结构:

type urlset struct {
    XMLName xml.Name `xml:"urlset"`
    URL     []struct {
        Loc  string `xml:"loc"`
        News struct {
            Publishdate string `xml:"publication_date"`
            Title       string `xml:"title"`
            Summary     string `xml:"keywords"`
        } `xml:"news"`
    } `xml:"url"`
}

如果我想推广嵌套结构“新闻”中的字段,该怎么做?

我希望可以直接访问“新闻”中的字段并打印其值,如下所示:

var URLset urlset
if xmlBytes, err := getXML(url); err != nil {
    fmt.Printf("Failed to get XML: %v", err)
} else {
    xml.Unmarshal(xmlBytes, &URLset)
}
/************************** XML parser *************************/
for _, URLElement := range URLset.URL {
fmt.Println(
    "[Element]:",
    "\nTitle #", URLElement.title,
    "\nPublicationDate #", URLElement.Publishdate,
    "\nSummary#", URLElement.Summary,
        "\nLoc #", URLElement.Loc, "\n")
}

这是完整的代码,我的Go Playground链接。

1
我已经运行了你的代码,没有问题。 - Rahmat Fathoni
1
@RahmatFathoni 是的,捕获 code 并在本地运行确实没有问题。 如果我没理解错的话,他试图运行最后一行被注释掉的代码。 - Carson
2
为了提升结构体的字段,您需要嵌入结构体类型。您只能嵌入命名类型和指向命名类型的指针。因此,您需要声明News结构体类型。然后,要嵌入该字段,您只需声明该字段不带显式字段名称,即仅使用字段的类型声明该字段。但这将使Go结构与XML不兼容,因此您需要在提升字段的标记中添加news>。另一种选择是在URL结构中声明所有字段,并在标记中使用elem1>elem2...来指导解组器。 - mkopriva
2
...示例 - mkopriva
1
@Carson,我明白了。你是对的。 - Rahmat Fathoni
1个回答

1
省略名称将嵌入到自身。
例如:
package main

import "fmt"

type Animal struct {
    Name string
}

type Cat struct {
    Animal //  Omit name
}

type Dog struct {
    A Animal //  have name "A"
}

func main() {
    cat := Cat{Animal{"Kitty"}}
    fmt.Println(cat.Name)        // OK
    fmt.Println(cat.Animal.Name) // OK too
    dog := Dog{Animal{"Snoopy"}}
    // fmt.Println(dog.Name)   // Error: type Dog has no field or method Name
    fmt.Println(dog.A.Name) // OK too
}

Go Playground


你的情况

看起来已经足够了(其他人也一样)。

package main

import (
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "net/http"
)

type News struct { //  move to here
    Publishdate string `xml:"news>publication_date"` //  Use ">" to tell its parent. https://github.com/golang/go/blob/0a1a092c4b56a1d4033372fbd07924dad8cbb50b/src/encoding/xml/typeinfo.go#L198-L199
    Title       string `xml:"news>title"`
    Summary     string `xml:"news>keywords"`
}

type urlset struct {
    XMLName xml.Name `xml:"urlset"`
    URL     []struct {
        Loc  string `xml:"loc"`
        News `xml:"news"` //  do not give the name
    } `xml:"url"`
}

func getXML(url string) ([]byte, error) {
    resp, err := http.Get(url)
    if err != nil {
        return []byte{}, fmt.Errorf("GET error: %v", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return []byte{}, fmt.Errorf("Status error: %v", resp.StatusCode)
    }

    data, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return []byte{}, fmt.Errorf("Read body: %v", err)
    }
    return data, nil
}

func main() {

    var URLset urlset
    /* To avoid the link not working in the future, I write the value directly.
    url := "https://www.dw.com/de/news-sitemap.xml"
    if xmlBytes, err := getXML(url); err != nil {
        fmt.Printf("Failed to get XML: %v", err)
    } else {
        xml.Unmarshal(xmlBytes, &URLset)
    }
    */

    xmlBytes := []byte(`
<urlset>
    <url>
        <loc>https://www.dw.com/de/kopf-an-kopf-rennen-bei-parlamentswahl-in-australien/a-61887162</loc>
        <news:news>
            <news:publication>
                <news:name>Deutsche Welle</news:name>
                <news:language>de</news:language>
            </news:publication>
            <news:publication_date>2022-05-21T11:28:55.875Z</news:publication_date>
            <news:title>Kopf-an-Kopf-Rennen bei Parlamentswahl in Australien</news:title>
            <news:keywords>Australien,Parlamentswahl,Scott Morrison,Anthony Albanese,Labor-Partei,Liberale</news:keywords>
        </news:news>
        <image:image>
            <image:loc>https://static.dw.com/image/61872101_403.jpg</image:loc>
            <image:caption>Der australische Premierminister Scott Morrison (r.) und sein Herausforderer, Oppositionsführer Anthony Albanese</image:caption>
        </image:image>
    </url>
    <url>
        <loc>https://www.dw.com/de/ukraine-aktuell-selenskyj-verlangt-entsch%C3%A4digungsfonds/a-61885143</loc>
        <news:news>
            <news:publication>
                <news:name>Deutsche Welle</news:name>
                <news:language>de</news:language>
            </news:publication>
            <news:publication_date>2022-05-21T11:10:21.813Z</news:publication_date>
            <news:title>Ukraine aktuell: Selenskyj verlangt Entschädigungsfonds</news:title>
            <news:keywords>Ukraine,Krieg,Russland,Wolodymyr Selenskyj,Wladimir Putin,Mariupol</news:keywords>
        </news:news>
        <image:image>
            <image:loc>https://static.dw.com/image/61885205_403.jpg</image:loc>
            <image:caption>75. Filmfestival Cannes | Rede von Wolodymyr Selenskyj</image:caption>
        </image:image>
    </url>
<urlset>
`)

    xml.Unmarshal(xmlBytes, &URLset)
    /************************** XML parser *************************/
    for _, URLElement := range URLset.URL {
        /*
           fmt.Println(
               "[Element]:",
               "\nTitle #", URLElement.News.Title,
               "\nPublicationDate #", URLElement.News.Publishdate,
               "\nSummary#", URLElement.News.Summary,
               "\nLoc #", URLElement.Loc, "\n")
        */

        fmt.Println( //  Now, this work!
            "[Element]:",
            "\nTitle #", URLElement.Title,
            "\nPublicationDate #", URLElement.Publishdate,
            "\nSummary#", URLElement.Summary,
            "\nLoc #", URLElement.Loc, "\n")
    }
}

关于XML命名空间

更多例子

  • ExampleUnmarshal 这个链接来自于 go/src/encoding/xml/example_test.go 实际上,所有的例子都很容易理解。所以这对学习很有帮助。

我将 'type News struct' 更改为:type News struct { Publishdate string xml:"news>publication_date" Title string xml:"news>title" Summary string xml:"news>keywords" },添加了 'news>' 部分,没有这个部分,解决方案无法正常工作。我还检查了 playground 代码,那个代码可以正常工作 :) - Jia
你介意纠正这个结构体定义吗?我没有权限去做,我会在你纠正后接受答案,以防误导他人。 - Jia
嗨@Jia,谢谢你的提醒。我只注意到它正常工作了,没有注意到一些值没有呈现出来。我已经更新了我的答案。 - Carson
你非常有礼貌。但我相信对帖子的修改是出于善意,不需要原作者的特别许可。不过,很快你就可以直接进行更改,而无需再经过社区审核。[2,000声望:编辑问题和答案] (https://stackoverflow.com/help/privileges/edit) - Carson
谢谢提供这些文档,真的很有帮助! - Jia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接