马歇尔在Go语言中将地图映射为XML。

huangapple go评论117阅读模式
英文:

Marshall map to XML in Go

问题

我正在尝试将地图输出为XML数据,但是我收到了以下错误信息:

  1. xml: 不支持的类型: map[string]int

将地图编组为JSON时可以正常工作,所以我不明白为什么在XML中不能使用相同的方法。难道只能使用结构体吗?

英文:

I'm trying to output a map as XML data, however I receive the following error:

  1. 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所建议的那样。

  1. // StringMap是一个map[string]string类型。
  2. type StringMap map[string]string
  3. // StringMap可以被编组为XML。
  4. func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  5. tokens := []xml.Token{start}
  6. for key, value := range s {
  7. t := xml.StartElement{Name: xml.Name{"", key}}
  8. tokens = append(tokens, t, xml.CharData(value), xml.EndElement{t.Name})
  9. }
  10. tokens = append(tokens, xml.EndElement{start.Name})
  11. for _, t := range tokens {
  12. err := e.EncodeToken(t)
  13. if err != nil {
  14. return err
  15. }
  16. }
  17. // 刷新以确保标记被写入
  18. return e.Flush()
  19. }

现在,可以通过简单地调用以下代码来编组该映射:

  1. output, err := xml.MarshalIndent(data, "", " ")
英文:

I ended up solving this by using the xml.Marshaler as suggested by Dave C

<!-- language: go -->

  1. // StringMap is a map[string]string.
  2. type StringMap map[string]string
  3. // StringMap marshals into XML.
  4. func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  5. tokens := []xml.Token{start}
  6. for key, value := range s {
  7. t := xml.StartElement{Name: xml.Name{&quot;&quot;, key}}
  8. tokens = append(tokens, t, xml.CharData(value), xml.EndElement{t.Name})
  9. }
  10. tokens = append(tokens, xml.EndElement{start.Name})
  11. for _, t := range tokens {
  12. err := e.EncodeToken(t)
  13. if err != nil {
  14. return err
  15. }
  16. }
  17. // flush to ensure tokens are written
  18. return e.Flush()
  19. }

<sub>Source: https://gist.github.com/jackspirou/4477e37d1f1c043806e0&lt;/sub>

Now the map can be marshalled by simply calling

<!-- language: go -->

  1. output, err := xml.MarshalIndent(data, &quot;&quot;, &quot; &quot;)

答案2

得分: 5

你可以对map进行编组和解组,但需要为你的map编写自定义的MarshalXML和UnmarshalXML函数,并为你的map附加这些函数的类型。

这是一个示例,其中map中的键和值都是字符串。你只需将值的编组更改为int => string,并在解组时进行相反的操作:
https://play.golang.org/p/4Z2C-GF0E7

  1. package main
  2. import (
  3. "encoding/xml"
  4. "fmt"
  5. "io"
  6. )
  7. type Map map[string]string
  8. type xmlMapEntry struct {
  9. XMLName xml.Name
  10. Value string `xml:",chardata"`
  11. }
  12. // MarshalXML将map编组为XML,其中map中的每个键都是一个标签,其对应的值是其内容。
  13. func (m Map) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  14. if len(m) == 0 {
  15. return nil
  16. }
  17. err := e.EncodeToken(start)
  18. if err != nil {
  19. return err
  20. }
  21. for k, v := range m {
  22. e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})
  23. }
  24. return e.EncodeToken(start.End())
  25. }
  26. // UnmarshalXML将XML解组为字符串到字符串的map,为map中的每个标签创建一个键,并将其值设置为标签的内容。
  27. //
  28. // 这个函数在Map的指针上是重要的,这样如果m为nil,它可以被初始化,这在m嵌套在另一个xml结构中时经常发生。这也是为什么第一行的第一件事是初始化它的原因。
  29. func (m *Map) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  30. *m = Map{}
  31. for {
  32. var e xmlMapEntry
  33. err := d.Decode(&e)
  34. if err == io.EOF {
  35. break
  36. } else if err != nil {
  37. return err
  38. }
  39. (*m)[e.XMLName.Local] = e.Value
  40. }
  41. return nil
  42. }
  43. func main() {
  44. // Map
  45. m := map[string]string{
  46. "key_1": "Value One",
  47. "key_2": "Value Two",
  48. }
  49. fmt.Println(m)
  50. // 编组为XML
  51. x, _ := xml.MarshalIndent(Map(m), "", " ")
  52. fmt.Println(string(x))
  53. // 从XML解组回来
  54. var rm map[string]string
  55. xml.Unmarshal(x, (*Map)(&rm))
  56. fmt.Println(rm)
  57. }

希望对你有帮助!

英文:

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

  1. package main
  2. import (
  3. &quot;encoding/xml&quot;
  4. &quot;fmt&quot;
  5. &quot;io&quot;
  6. )
  7. type Map map[string]string
  8. type xmlMapEntry struct {
  9. XMLName xml.Name
  10. Value string `xml:&quot;,chardata&quot;`
  11. }
  12. // MarshalXML marshals the map to XML, with each key in the map being a
  13. // tag and it&#39;s corresponding value being it&#39;s contents.
  14. func (m Map) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  15. if len(m) == 0 {
  16. return nil
  17. }
  18. err := e.EncodeToken(start)
  19. if err != nil {
  20. return err
  21. }
  22. for k, v := range m {
  23. e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})
  24. }
  25. return e.EncodeToken(start.End())
  26. }
  27. // UnmarshalXML unmarshals the XML into a map of string to strings,
  28. // creating a key in the map for each tag and setting it&#39;s value to the
  29. // tags contents.
  30. //
  31. // The fact this function is on the pointer of Map is important, so that
  32. // if m is nil it can be initialized, which is often the case if m is
  33. // nested in another xml structurel. This is also why the first thing done
  34. // on the first line is initialize it.
  35. func (m *Map) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  36. *m = Map{}
  37. for {
  38. var e xmlMapEntry
  39. err := d.Decode(&amp;e)
  40. if err == io.EOF {
  41. break
  42. } else if err != nil {
  43. return err
  44. }
  45. (*m)[e.XMLName.Local] = e.Value
  46. }
  47. return nil
  48. }
  49. func main() {
  50. // The Map
  51. m := map[string]string{
  52. &quot;key_1&quot;: &quot;Value One&quot;,
  53. &quot;key_2&quot;: &quot;Value Two&quot;,
  54. }
  55. fmt.Println(m)
  56. // Encode to XML
  57. x, _ := xml.MarshalIndent(Map(m), &quot;&quot;, &quot; &quot;)
  58. fmt.Println(string(x))
  59. // Decode back from XML
  60. var rm map[string]string
  61. xml.Unmarshal(x, (*Map)(&amp;rm))
  62. fmt.Println(rm)
  63. }

答案3

得分: 0

我猜想是因为XML节点是有序的,而map是无序的。请查看这个链接

  1. Marshal通过对每个元素进行编组来处理数组或切片
英文:

I suppose that because XML nodes is sequenced, but map is not. check this

  1. Marshal handles an array or slice by marshalling each of the elements.

huangapple
  • 本文由 发表于 2015年6月19日 10:03:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/30928770.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定