2016-09-29 172 views
2

我試圖創建一個實現MarshalXML輸出的XML。 但目前我正面臨幾個問題。Golang 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>標籤(使用標準的封送它即使它是空的)。

所以我所做的是:

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

而且它顯然作品,如果我用它的編碼單版實體。 但是,如果我嘗試編碼版實體的數組,我得到以下錯誤:

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

(數組是〜200個)

所以我不明白的是:

  • 爲什麼堆棧溢出問題只發生在我嘗試自定義xml時出現,在這種情況下,它也試圖刪除空標籤,所以「節省空間」
  • 這樣做的最佳方式是什麼?有人可以向我解釋如何實現一個定製的XML Marshaller去?我發現很多JSON元帥,但幾乎沒有任何XML)
+0

根據你的堆棧溢出,你導致了一個無限循環,所以EncodeElement調用了正在調用EncodeElement的MarshalXML。 – GarMan

+0

好的,但是爲什麼只有嘗試編組一個版本元素纔會發生這種情況,而不是如果我編組單個元素? – Ivan

回答

3

好吧,我會回答自己,因爲我終於解決了這個問題。

所以顯然,問題之一是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. 最後您可以關閉開始標籤。

以這種方式,將生成的XML標籤唯一的數據是否可用,並添加屬性/孩子的,只有當他們有一個值,如果它們是空或0它不會被添加。