在 Go 中 Marshall 映射到 XML
Marshall map to XML in Go
我正在尝试将地图输出为 XML 数据,但是我收到以下错误:
xml: unsupported type: map[string]int
编组映射适用于 JSON,所以我不明白为什么它不适用于 XML。使用结构真的是唯一的方法吗?
我想是因为 XML 节点是有序的,但地图不是。检查 this
Marshal handles an array or slice by marshalling each of the elements.
我最终按照 Dave C
的建议使用 xml.Marshaler 解决了这个问题
// 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
return e.Flush()
}
来源:https://gist.github.com/jackspirou/4477e37d1f1c043806e0
现在只需调用
即可编组地图
output, err := xml.MarshalIndent(data, "", " ")
您可以编组和取消编组地图,但您需要为您的地图编写自定义 MarshalXML 和 UnmarshalXML 函数,并为您提供一个类型以将这些函数附加到。
这是一个编组和解编组的示例,其中映射中的键和值是一个字符串。您可以简单地将值的 marshal 更改为 int => string 并返回到 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's corresponding value being it's 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's 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)
}
我正在尝试将地图输出为 XML 数据,但是我收到以下错误:
xml: unsupported type: map[string]int
编组映射适用于 JSON,所以我不明白为什么它不适用于 XML。使用结构真的是唯一的方法吗?
我想是因为 XML 节点是有序的,但地图不是。检查 this
Marshal handles an array or slice by marshalling each of the elements.
我最终按照 Dave C
的建议使用 xml.Marshaler 解决了这个问题// 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
return e.Flush()
}
来源:https://gist.github.com/jackspirou/4477e37d1f1c043806e0
现在只需调用
即可编组地图output, err := xml.MarshalIndent(data, "", " ")
您可以编组和取消编组地图,但您需要为您的地图编写自定义 MarshalXML 和 UnmarshalXML 函数,并为您提供一个类型以将这些函数附加到。
这是一个编组和解编组的示例,其中映射中的键和值是一个字符串。您可以简单地将值的 marshal 更改为 int => string 并返回到 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's corresponding value being it's 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's 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)
}