英文:
Marshall map to XML in Go
问题
我正在尝试将地图输出为XML数据,但是我收到了以下错误信息:
xml: 不支持的类型: map[string]int
将地图编组为JSON时可以正常工作,所以我不明白为什么在XML中不能使用相同的方法。难道只能使用结构体吗?
英文:
I'm trying to output a map as XML data, however I receive the following error:
xml: unsupported type: map[string]int
Marshalling maps works fine for JSON so I don't get why it wouldn't work the same for XML. Is using a Struct really the only way?
答案1
得分: 19
我通过使用xml.Marshaler来解决了这个问题,正如Dave C所建议的那样。
// StringMap是一个map[string]string类型。
type StringMap map[string]string
// StringMap可以被编组为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
}
}
// 刷新以确保标记被写入
return e.Flush()
}
现在,可以通过简单地调用以下代码来编组该映射:
output, err := xml.MarshalIndent(data, "", " ")
英文:
I ended up solving this by using the xml.Marshaler as suggested by Dave C
<!-- language: go -->
// 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()
}
<sub>Source: https://gist.github.com/jackspirou/4477e37d1f1c043806e0</sub>
Now the map can be marshalled by simply calling
<!-- language: go -->
output, err := xml.MarshalIndent(data, "", " ")
答案2
得分: 5
你可以对map进行编组和解组,但需要为你的map编写自定义的MarshalXML和UnmarshalXML函数,并为你的map附加这些函数的类型。
这是一个示例,其中map中的键和值都是字符串。你只需将值的编组更改为int => string,并在解组时进行相反的操作:
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将map编组为XML,其中map中的每个键都是一个标签,其对应的值是其内容。
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将XML解组为字符串到字符串的map,为map中的每个标签创建一个键,并将其值设置为标签的内容。
//
// 这个函数在Map的指针上是重要的,这样如果m为nil,它可以被初始化,这在m嵌套在另一个xml结构中时经常发生。这也是为什么第一行的第一件事是初始化它的原因。
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() {
// Map
m := map[string]string{
"key_1": "Value One",
"key_2": "Value Two",
}
fmt.Println(m)
// 编组为XML
x, _ := xml.MarshalIndent(Map(m), "", " ")
fmt.Println(string(x))
// 从XML解组回来
var rm map[string]string
xml.Unmarshal(x, (*Map)(&rm))
fmt.Println(rm)
}
希望对你有帮助!
英文:
You can marshal and unmarshal a map, but you need to write the custom MarshalXML and UnmarshalXML function for your map and give you map a type to attach those functions to.
Here's an example that marshals and unmarshals where the key and the value in the map is a string. You can simply change the marshal of the value to int => string and back in the 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)
}
答案3
得分: 0
我猜想是因为XML节点是有序的,而map是无序的。请查看这个链接。
Marshal通过对每个元素进行编组来处理数组或切片。
英文:
I suppose that because XML nodes is sequenced, but map is not. check this
Marshal handles an array or slice by marshalling each of the elements.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论