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 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动
  • ¥20 对于工程问题的非线性数学模型进行线性化
  • ¥15 Mirare PLUS 进行密钥认证?(详解)
  • ¥15 物体双站RCS和其组成阵列后的双站RCS关系验证
  • ¥20 想用ollama做一个自己的AI数据库
  • ¥15 关于qualoth编辑及缝合服装领子的问题解决方案探寻
  • ¥15 请问怎么才能复现这样的图呀