Golang XML 自定义输出

Iva*_*van 4 xml marshalling go

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

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

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"`
}
Run Code Online (Sandbox Code Playgroud)

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

所以我所做的是:

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

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

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow
Run Code Online (Sandbox Code Playgroud)

(数组是 ~200 个条目)

所以我不明白的是:

  • 为什么堆栈溢出问题仅在我尝试自定义 xml 时发生,在这种情况下也尝试删除空标签,因此“节省空间”
  • 最好的方法是什么?有人可以解释我如何为 go 实现自定义 XML 封送拆收器?我发现了很多 JSON marshal,但几乎没有 XML)

Iva*_*van 5

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

显然,问题之一是 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})
}
Run Code Online (Sandbox Code Playgroud)

所以我所做的是:

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

这样,它只会在数据可用时生成 xml 标记,并且仅当属性/子项具有值时才添加属性/子项,如果它们为空或 0,则不会添加。