duankangpazhuo0347 2016-09-13 14:03
浏览 83
已采纳

具有相同标签的Golang XML创建

So I've got this specific format for xml (industry standard) and I'm trying to create a simple program to allow us to make samples of this xml to test our services. I'm using the standard Go XML library.

Problem is the XML is annoyingly formatted. Here's the simplified version of it:

<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>

What I'm struggling with is that in Go, the tags on each section need to be unique if they are different struct types.

I've the following Go code:

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"`
}

I've also tried to have the Sections field have no tag and have both FooSection and BarSection have the

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

tag, to no avail. Furthermore, I've tried having Sections be an array of strings and then marshaled each section type, dumped those in and used the ",innerxml" tag, but then it escapes the "<", etc of the innerxml.

Does anyone know a way to do this in Go? The structs are written by me and are completely open to change if need be.

It might just be that I'm too entrenched in OO and am having trouble being Go-like.

Thanks!

  • 写回答

1条回答 默认 最新

  • drvntaomy06331839 2016-09-13 16:35
    关注

    I don't know if this is a perfect answer but it's workable. The gist is to implement encoding/xml.Unmarshaler on the Component type then inside that UnmarshalXML method you unmarshal the raw data of the section into a temporary value and inspect its ID before deciding if you want to unmarshal it into a FooSection or BarSection

    These are the types I'm working with

    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"`
    }
    

    Note that Component is storing its Section as just an interface{}. That is kind of annoying because you'll have to type switch it whenever you want to use it so you can probably do something better with that.

    Then the UnmarshalXML method is here

    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
    }
    

    Full working code on the playground.


    Edit: These types should also work for generating the XML string as you actually were asking. When you're constructing each Component you should choose what kind of section to make and just stick it in there. Since it's a interface{} it can hold anything. I've updated my playground link to an example that shows turning those types back into a string works as expected.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 多址通信方式的抗噪声性能和系统容量对比
  • ¥15 winform的chart曲线生成时有凸起
  • ¥15 msix packaging tool打包问题
  • ¥15 finalshell节点的搭建代码和那个端口代码教程
  • ¥15 Centos / PETSc / PETGEM
  • ¥15 centos7.9 IPv6端口telnet和端口监控问题
  • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作
  • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 海浪数据 南海地区海况数据,波浪数据
  • ¥20 软件测试决策法疑问求解答