Golang XML自定义输出

时间:2016-09-29 21:37:44

标签: xml go marshalling

我正在尝试创建一个实现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"`
}

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

所以我做的是:

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

如果我用它来编码单个版本的实体,它显然是有效的。 但是如果我尝试编码一个Edition实体数组,我会收到以下错误:

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

(数组约200个条目)

所以我不明白的是:

  • 为什么只有在我尝试自定义xml时才会发生堆栈溢出问题,在这种情况下也会尝试删除空标记,以便“节省空间”
  • 最好的方法是什么?有人可以解释我如何为go实现自定义XML Marshaler?我找到了很多JSON元帅,但几乎没有XML格式)

1 个答案:

答案 0 :(得分:4)

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

显然,其中一个问题是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. 检查字段是否为空,如果是,则返回null(与我的问题相同)
  2. 如果不为空,请检查PreconstructedInfo中包含的值,并将它们添加到相关位置,将第一个属性添加到start元素。 start.Attr将包含Marshalled标记的xml属性,它的语法非常简单,您可以指定名称和值。这必须在调用e.EncodeToken(start)之前完成。
  3. 之后将标签的其他元素编码为当前的开始元素。如您所见,您必须使用xml.StartElement对标记进行编码,方式与属性类似。
  4. 最后,您可以关闭开始标记。
  5. 这样,只有在数据可用时才会生成xml标记,并且只有在有值的情况下才添加属性/子元素,如果它们为空或0则不添加。