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 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 下图接收小电路,谁知道原理
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度