在 Go 中可配置 xml 解组

Configurable xml unmarshalling in Go

我正在编写一个 xml 解析器,我希望将哪些数据映射到我自己的格式。我可以通过使用 xml.Unmarshal() 并定义一个结构以及我想要保留的字段来做到这一点。

即对于典型的 RSS 提要,我会这样定义结构:

type ChannelRss struct {
    XMLName xml.Name `xml:"rss"`
    NewsItems []struct {
        Headline    string `xml:"title"`
        Intro       string `xml:"description"`
        ArticleID   string `xml:"guid"`
    } `xml:"channel>item"`
}

目前没问题。我可以创建一个函数,它接受 xml-data 和 returns 一个 ChannelRss,供我处理。但是,如果我想解析多个 xml/rss 提要并将它们映射到类似的方式怎么办?我将不得不创建一个具有不同配置的新结构:


type TheVergeRss struct {
    XMLName xml.Name `xml:"feed"`
    NewsItems []struct {
        Headline    string `xml:"title"`
        Intro       string `xml:"content"`
        ArticleID   string `xml:"guid"`
    } `xml:"entry"`
}

但这给我留下了一个问题,我现在有 2(+) 个不同的结构,它们基本上包含相同的数据:标题、简介和 ArticleID。有没有办法 return 通用结构?:

基本上我想把所有 field-tags 放在配置文件中。并且有一个接受 url 的函数,伴随着匹配 field-tags,并且只映射到这样的结构。

type NewsItemCollection struct {
    XMLName xml.Name
    NewsItem []struct {
        Headline string
        Intro string
        ArticleID string
    }
}

有什么好的方法吗?

一种方法是使用反射。您可以使用 reflect.StructOf 动态生成具有所需标签的结构,解组 xml 并将此动态类型转换为静态类型,例如:

//define the static struct
type StaticStruct struct {
    Headline   string
    Intro      string
    ArticleID  string
}
//define a dynamic struct
dynamicType := reflect.StructOf([]reflect.StructField{
    {
      Name: "Headline",
      Type: reflect.TypeOf("str"),
      Tag:  `xml:"title"`,
    },
    {
      Name: "Intro",
      Type: reflect.TypeOf("str"),
      Tag:  `xml:"content"`,
    },
    {
      Name: "ArticleID",
      Type: reflect.TypeOf("str"),
      Tag:  `xml:"guid"`,
    },
  })
dynamicInstance := reflect.New(dynamicType)
//unmarshal content into new dynamicInstance (it's already a pointer)
xml.Unmarshal(`xml content`, dynamicInstance.Interface())
//Convert dynamic instance into a StaticStruct
staticInstance := dynamicInstance.Convert(reflect.TypeOf(&StaticStruct{})).Interface()

转换将起作用,因为 StaticStruct 具有与 dynamicType 指定的字段相同的字段,忽略结构标签