Marshall在Go中映射到XML

Ada*_*m B 6 xml marshalling go

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

xml: unsupported type: map[string]int
Run Code Online (Sandbox Code Playgroud)

编组映射适用于JSON,因此我不明白为什么它对XML不起作用.使用Struct真的是唯一的方法吗?

Ada*_*m B 10

我最后通过使用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
    err := e.Flush()
    if err != nil {
        return err
    }

    return nil
}
Run Code Online (Sandbox Code Playgroud)

资料来源:https://gist.github.com/jackspirou/4477e37d1f1c043806e0

现在可以通过简单地调用来编组地图

output, err := xml.MarshalIndent(data, "", "  ")
Run Code Online (Sandbox Code Playgroud)


Lei*_*och 6

您可以编组和解组映射,但需要为映射编写自定义 MarshalXML 和 UnmarshalXML 函数,并为映射提供一个类型来附加这些函数。

下面是一个编组和解组的示例,其中映射中的键和值是字符串。您可以简单地将值的编组更改为 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 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)
}
Run Code Online (Sandbox Code Playgroud)