Reputation: 4234
I'm trying to create an XML implementing the MarshalXML output. But currently i'm facing several issues.
The structure i'm using for storing the data is:
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"`
}
What i want is:
If the Preconstructed field is not set, don't put the <preconstructed>
tag (using the standard marshaler it put it even if it is empty).
So what i did is:
func (preconstructed PreconstructedInfo) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if (PreconstructedInfo{} == preconstructed) {
return nil
}
return e.EncodeElement(preconstructed, start)
}
And it apparently works, if I use it for encoding a single Edition entity. But if I try to encode an array of Edition entities, I get the following error:
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow
(the array is ~200 entries)
So what I don't understand is:
Upvotes: 4
Views: 4435
Reputation: 130
This should prevent the infinite recursion -
func (preconstructed PreconstructedInfo) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if (PreconstructedInfo{} == preconstructed) {
return nil
}
type localType PreconstructedInfo
var localVar PreconstructedInfo = localType(preconstructed)
return e.EncodeElement(localVar, start)
}
Basically we're creating a local type (localType
) that's identical to the type being marshaled (PreconstructedInfo
), and create a variable of this type (localVar
) having the same data. Then we run EncodeElement
on the local variable of the local type, thereby preventing an infinite loop.
I learnt this trick from here -
https://jhall.io/posts/go-json-tricks-slightly-custom-marshaler/#breaking-the-loop-with-a-local-type
Upvotes: 2
Reputation: 4234
Ok, i'll answer myself since i finally solved that issue.
So apparently one of the problem is that EncodeElement is making use of MarshalXML, and with a huge file it cause an explosion function calls.
Anyway the solution is to manually encode all components of the element.
So in that case i did that:
// 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})
}
So what i did is:
In that way it will generate the xml tag only if data is available, and add attributes/childs only if they have a value, if they are empty or 0 it will not be added.
Upvotes: 5