Marshall Map in XML in Go

I am trying to output the map as XML data, however I am getting the following error:

xml: unsupported type: map[string]int 

Marshalling cards work fine for JSON, so I don’t understand why this will not work for XML. Does Struct use only one way?

+5
source share
3 answers

I decided to solve this using xml.Marshaler, as suggested by Dave C

 // StringMap is a map[string]string. type StringMap map[string]string // StringMap marshals into XML. func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { tokens := []xml.Token{start} for key, value := range s { t := xml.StartElement{Name: xml.Name{"", key}} tokens = append(tokens, t, xml.CharData(value), xml.EndElement{t.Name}) } tokens = append(tokens, xml.EndElement{start.Name}) for _, t := range tokens { err := e.EncodeToken(t) if err != nil { return err } } // flush to ensure tokens are written err := e.Flush() if err != nil { return err } return nil } 

Source: https://gist.github.com/jackspirou/4477e37d1f1c043806e0

Now the card can be combined simply by calling

 output, err := xml.MarshalIndent(data, "", " ") 
+10
source

I assume that since the XML nodes are sequenced and the map is not. check out this Marshal handles an array or slice by marshalling each of the elements.

0
source

You can marshal and untie the map, but you need to write the custom function MarshalXML and UnmarshalXML for your map and give you a map of the type to attach these functions.

Here is an example in which marshals and non-marshals, where the key and value on the map are a string. You can simply change the marshal of the value to int => string and back to unmarshal: https://play.golang.org/p/4Z2C-GF0E7

 package main import ( "encoding/xml" "fmt" "io" ) type Map map[string]string type xmlMapEntry struct { XMLName xml.Name Value string `xml:",chardata"` } // MarshalXML marshals the map to XML, with each key in the map being a // tag and it corresponding value being it contents. func (m Map) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if len(m) == 0 { return nil } err := e.EncodeToken(start) if err != nil { return err } for k, v := range m { e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v}) } return e.EncodeToken(start.End()) } // UnmarshalXML unmarshals the XML into a map of string to strings, // creating a key in the map for each tag and setting it value to the // tags contents. // // The fact this function is on the pointer of Map is important, so that // if m is nil it can be initialized, which is often the case if m is // nested in another xml structurel. This is also why the first thing done // on the first line is initialize it. func (m *Map) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { *m = Map{} for { var e xmlMapEntry err := d.Decode(&e) if err == io.EOF { break } else if err != nil { return err } (*m)[e.XMLName.Local] = e.Value } return nil } func main() { // The Map m := map[string]string{ "key_1": "Value One", "key_2": "Value Two", } fmt.Println(m) // Encode to XML x, _ := xml.MarshalIndent(Map(m), "", " ") fmt.Println(string(x)) // Decode back from XML var rm map[string]string xml.Unmarshal(x, (*Map)(&rm)) fmt.Println(rm) } 
0
source

All Articles