Golang XML Creation使用相同的标签

时间:2016-09-13 14:03:20

标签: xml go

因此,我已经获得了xml(行业标准)的特定格式,并且我尝试创建一个简单的程序,以便我们可以对此xml进行测试以测试我们的服务。我正在使用标准的Go XML库。

问题是XML格式化了。 这是它的简化版本:

<Document>
  <SubDocument>
    {other fields}
    <component>
         <section>
             <id value="0" valueType="num"/> //This is the part that differentiates the type of the section
             <title>Foo-Type Section</title>
             {other fields you lot don't need to care about}
         </section>
    </component>
    <component>
         <section>
             <id value="1" valueType="num"/>
             <title>Bar-Type Section</title>
             {more fields you don't need to care about, but most are different than above}
         </section>
    </component>
    {more sections}
  </SubDocument>
</Document>

我挣扎的是,在Go中,每个部分的标签如果是不同的结构类型,则必须是唯一的。

我有以下Go代码:

type HasID struct{
    ID   string `xml:"value,attr,omitempty"`
    IDType    string `xml:"valueType,attr,omitempty"`
}
type FooSection struct{
     ID   HasID `xml:"id,omitempty"`
     Title string `xml:"title,omitempty"`
     //Foo fields
}
type BarSection struct{
     ID   HasID `xml:"id,omitempty"`
     Title string `xml:"title,omitempty"`
     //Bar fields
}
type Document struct{
    XMLName  struct{} `xml:"Document,omitempty"`
    //Other fields
    Sections  []interface{} `xml:"SubDocument>component>section,omitempty"`
}

我还尝试让Sections字段没有标记,并且FooSection和BarSection都有

XMLName  struct{} `xml:"component>section,omitempty"`

标签,无济于事。此外,我尝试让Sections成为一个字符串数组,然后编组每个部分类型,将其转储并使用&#34;,innerxml&#34; tag,但它会逃脱innerxml的&#34;&lt;&#34;等。

有人知道在Go中这样做的方法吗?结构由我编写,如果需要,可以完全改变。

可能只是因为我在OO中根深蒂固而且我很难成为Go-like。

谢谢!

1 个答案:

答案 0 :(得分:1)

我不知道这是否是一个完美的答案,但它是可行的。要点是在Component类型上实现encoding/xml.Unmarshaler然后在UnmarshalXML方法内部将该部分的原始数据解组为临时值并检查其ID,然后再决定是否要解组它变为FooSectionBarSection

这些是我使用

的类型
type ID struct {
    Value int    `xml:"value,attr,omitempty"`
    Type  string `xml:"valueType,attr,omitempty"`
}

type Document struct {
    Components []Component `xml:"SubDocument>component"`
}

type Component struct {
    Section interface{} `xml:"section"`
}

type FooSection struct {
    ID    ID     `xml:"id"`
    Title string `xml:"title"`
    Foo   string `xml:"foo"`
}

type BarSection struct {
    ID    ID     `xml:"id"`
    Title string `xml:"title"`
    Bar   string `xml:"bar"`
}

请注意,Component仅将Section存储为interface{}。这有点令人讨厌,因为只要你想使用它就必须打开它,所以你可以用它做更好的事情。

然后UnmarshalXML方法就在这里

func (c *Component) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {

    // tmp holds the data for this Component. We can only call d.DecodeElement
    // once so we have to put it somewhere so it can be reused.
    tmp := struct {
        Data []byte `xml:",innerxml"`
    }{}
    if err := d.DecodeElement(&tmp, &start); err != nil {
        return err
    }

    // which holds just enough information to tell us what kind of section to
    // make. We'll unmarshal tmp.Data into this to inspect it
    which := struct {
        ID ID `xml:"id"`
    }{}
    if err := xml.Unmarshal(tmp.Data, &which); err != nil {
        return err
    }

    switch which.ID.Value {
    case 0:
        var f FooSection
        if err := xml.Unmarshal(tmp.Data, &f); err != nil {
            return err
        }
        c.Section = f

    case 1:
        var b BarSection
        if err := xml.Unmarshal(tmp.Data, &b); err != nil {
            return err
        }
        c.Section = b
    }

    return nil
}

完整的工作代码on the playground

编辑:这些类型也应该像您实际要求的那样生成XML字符串。当您构建每个Component时,您应该选择要制作的部分,并将其粘贴在那里。因为它是interface{}它可以容纳任何东西。我已经将我的游乐场链接更新为一个示例,该示例显示将这些类型转换回字符串按预期工作。