Golang XML 自定义输出

Golang XML customize output

我正在尝试创建一个 XML 实现 MarshalXML 输出。 但目前我面临着几个问题。

我用来存储数据的结构是:

type Edition struct {
    Launch         string             `xml:"launch" json:"launch"`
    Code           string             `xml:"code" json:"code"`
    Names          []NameNode         `xml:"names>name"`
    Cards          CardsComposition   `xml:"cards" json:"cards,omitempty"`
    Preconstructed PreconstructedInfo `xml:"preconstructed" json:"preconstructed,omitempty"`
    Vault          *struct{}          `xml:"vault" json:"vault"`
    Online         *struct{}          `xml:"online" json:"online"`
}

我想要的是: 如果未设置 Preconstructed 字段,请不要放置 <preconstructed> 标记(使用标准封送拆收器放置它,即使它是空的)。

所以我所做的是:

func (preconstructed PreconstructedInfo) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    if (PreconstructedInfo{} == preconstructed) {
        return nil
    }
    return e.EncodeElement(preconstructed, start)
}

如果我用它来编码单个 Edition 实体,它显然有效。 但是,如果我尝试对 Edition 实体数组进行编码,则会出现以下错误:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

(数组大约有 200 个条目)

所以我不明白的是:

好的,我会自己回答,因为我终于解决了那个问题。

很明显,问题之一是 EncodeElement 正在使用 MarshalXML,并且对于一个巨大的文件,它会导致函数调用激增。

无论如何,解决方案是手动对元素的所有组件进行编码。

所以在那种情况下我这样做了:

// MarshalXML generate XML output for PrecsontructedInfo
func (preconstructed PreconstructedInfo) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
    if (PreconstructedInfo{} == preconstructed) {
        return nil
    }
    if preconstructed.Decks > 0 {
        start.Attr = []xml.Attr{xml.Attr{Name: xml.Name{Local: "decks"}, Value: strconv.Itoa(preconstructed.Size)}}
    }
    if strings.Compare(preconstructed.Type, "") != 0 {
        start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "type"}, Value: preconstructed.Type})
    }

    err = e.EncodeToken(start)
    e.EncodeElement(preconstructed.Size, xml.StartElement{Name: xml.Name{Local: "size"}})
    return e.EncodeToken(xml.EndElement{Name: start.Name})
}

所以我所做的是:

  1. 检查该字段是否为空,如果是return null(与我的问题相同)
  2. 如果不为空,检查PreconstructedInfo中包含的值,并将它们添加到相应的位置,将first属性添加到start元素。 start.Attr 将包含被编组的标签的 xml 属性,它的语法很简单,您指定名称和值。这必须在调用 e.EncodeToken(start).
  3. 之前完成
  4. 之后将标签的其他元素编码到当前起始元素中。如您所见,您必须使用 xml.StartElement 以与属性类似的方式对标签进行编码。
  5. 终于可以关闭开始标签了。

这样只有在有数据的情况下才会生成xml标签,只有在有值的情况下才会添加attributes/childs,如果为空或0则不会添加。