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 个条目)
所以我不明白的是:
- 为什么只有当我尝试自定义 xml 时才会发生堆栈溢出问题,在这种情况下还试图删除空标签,所以 "saving space"
- 最好的方法是什么?有人可以向我解释如何为 go 实现自定义 XML 封送拆收器吗?我为 JSON 元帅找到了很多,但几乎没有为 XML)
好的,我会自己回答,因为我终于解决了那个问题。
很明显,问题之一是 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})
}
所以我所做的是:
- 检查该字段是否为空,如果是return null(与我的问题相同)
- 如果不为空,检查PreconstructedInfo中包含的值,并将它们添加到相应的位置,将first属性添加到start元素。 start.Attr 将包含被编组的标签的 xml 属性,它的语法很简单,您指定名称和值。这必须在调用 e.EncodeToken(start).
之前完成
- 之后将标签的其他元素编码到当前起始元素中。如您所见,您必须使用 xml.StartElement 以与属性类似的方式对标签进行编码。
- 终于可以关闭开始标签了。
这样只有在有数据的情况下才会生成xml标签,只有在有值的情况下才会添加attributes/childs,如果为空或0则不会添加。
我正在尝试创建一个 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 个条目)
所以我不明白的是:
- 为什么只有当我尝试自定义 xml 时才会发生堆栈溢出问题,在这种情况下还试图删除空标签,所以 "saving space"
- 最好的方法是什么?有人可以向我解释如何为 go 实现自定义 XML 封送拆收器吗?我为 JSON 元帅找到了很多,但几乎没有为 XML)
好的,我会自己回答,因为我终于解决了那个问题。
很明显,问题之一是 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})
}
所以我所做的是:
- 检查该字段是否为空,如果是return null(与我的问题相同)
- 如果不为空,检查PreconstructedInfo中包含的值,并将它们添加到相应的位置,将first属性添加到start元素。 start.Attr 将包含被编组的标签的 xml 属性,它的语法很简单,您指定名称和值。这必须在调用 e.EncodeToken(start). 之前完成
- 之后将标签的其他元素编码到当前起始元素中。如您所见,您必须使用 xml.StartElement 以与属性类似的方式对标签进行编码。
- 终于可以关闭开始标签了。
这样只有在有数据的情况下才会生成xml标签,只有在有值的情况下才会添加attributes/childs,如果为空或0则不会添加。