Reputation: 109
I need to parse an XML message that has a dynamic element, so I have used an element of type interface{}
to represent it in my Message struct.
Once I know the type of this dynamic element (at runtime) I initialise a message struct and then try to unmarshal the XML message. However, the contents of the dynamic element are not unmarshalled.
Here is a Go Playground with what I'm trying to achieve, with comments and the actual vs expected outputs: https://play.golang.org/p/eKVetUPmVI2
I tried several variations but can't get the unmarshalling to work as expected. Can anyone help me to understand why this behaviour and how to make it work? Thanks in advance.
Code (in case the Go Playground link breaks one day):
package main
import "fmt"
import "encoding/xml"
// XML root
type Message struct {
XMLName xml.Name `xml:"message"`
Operation Operation `xml:"operation"`
}
// An Operation can contain either a Create or an Update element
type Operation struct {
Create *Create `xml:"create"`
Update *Update `xml:"update"`
}
// Doesn't matter...
type Create struct{}
// Update contains a Color element or Any other element (we only know its type during runtime)
type Update struct {
Color *Color `xml:"color"`
Other Any
}
// Doesn't matter...
type Color struct{}
type Any interface{}
var xmlStr = []byte(`<message>
<operation>
<update>
<size>
<width>1000</width>
</size>
</update>
</operation>
</message>`)
func main() {
// At this point we already know what to expect to receive in Other, so we can declare a struct for its content (Size)
type Size struct {
XMLName xml.Name `xml:"size"`
Width string `xml:"width"`
}
// Unmarshal
msg := &Message{
Operation: Operation{
Update: &Update{
Other: &Size{}, // Here I'm setting Other to Size, so I would expect Go to unmarshal the <size> contents into it
},
},
}
if err := xml.Unmarshal(xmlStr, msg); err != nil {
fmt.Println(err)
}
// Marshal again
b, err := xml.MarshalIndent(msg, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Printf("expected:\n\n%s\n\n", xmlStr)
fmt.Printf("actual:\n\n%s", string(b))
}
Upvotes: 2
Views: 2816
Reputation: 4421
Per the encoding/xml
package documentation:
If the XML element contains a sub-element that hasn't matched any of the above rules and the struct has a field with tag
",any"
, unmarshal maps the sub-element to that struct field.
One small update to your code makes it work as you expected:
Add the xml:",any"
tag to your Other
field definition.
To clean up the code, I would also remove the Any
type, you don't need it. You can change the Other
field definition to type interface{}
with tag xml:",any"
and accomplish the same thing.
Like so:
Other interface{} `xml:",any"`
Execute and see the "1000" captured.
I suggest updating your question to include your code directly to make it easier for people to find/search/read your question. Having the Go playground link as well is also useful so readers can quickly run/tweak/test the example.
Upvotes: 5