Tengo un mapa: []map[string]string.

Rellenar los resultados en un objeto json.marshal() compatible. Salida:

[
  {
    "key1": "val1",
    "key2": "val2"
  },
  {
    "randval3": "val1",
    "randval2": "xyz1"
    "randval1": "xyz3"
  },
  ...
]

Sin embargo, cuando ejecuto xml.marshal(). Recibo un xml: unsupported type: map[string]string. Lo que parece razonable dado el hecho de que XML necesita nombres de nodo, etc. Entonces, básicamente, lo que estoy buscando es una forma de obtener:

<rootElement>
    <child>
        <key1>val1</key1>
        <key2>val1</key2>
    </child>
    <child>
        <randval3>val1</randval3>
        <randval2>xyz1</randval2>
        <randval1>xyz1</randval1>
    </child>
</rootElement>

Pero me estoy quedando estancado con la obtención de un 'objeto' compatible con xml.unmarshal()

0
Benjamin de Bos 17 abr. 2020 a las 11:11

2 respuestas

La mejor respuesta

Puede declarar un mapa personalizado y hacer que implemente la interfaz xml.Marshaler.

type mymap map[string]string

func (m mymap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    if err := e.EncodeToken(start); err != nil {
        return err
    }

    for key, val := range m {
        s := xml.StartElement{Name: xml.Name{Local: key}}
        if err := e.EncodeElement(val, s); err != nil {
            return err
        }
    }

    return e.EncodeToken(start.End())
}

type RootElement struct {
    XMLName  xml.Name `xml:"rootElement"`
    Children []mymap  `xml:"child"`
} 

https://play.golang.com/p/0_qA9UUvhKV

func main() {
    root := RootElement{Children: []mymap{
        {"key1": "val1", "key2": "val2"},
        {"randval1": "val1", "randval2": "xyz1", "randval3": "abc3"},
    }}

    data, err := xml.MarshalIndent(root, "", "  ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
}

Salida:

<rootElement>
  <child>
    <key2>val2</key2>
    <key1>val1</key1>
  </child>
  <child>
    <randval3>abc3</randval3>
    <randval1>val1</randval1>
    <randval2>xyz1</randval2>
  </child>
</rootElement>
0
mkopriva 17 abr. 2020 a las 09:48

Para los mapas Marshalling / Unmarshalling, debe escribir sus propias funciones Marshal() y Unmarshal().

A continuación se muestra un ejemplo para implementar estas funciones para type Maps []map[string]string y cómo usarlas.

type Maps []map[string]string

type xmlMapEntry struct {
    XMLName xml.Name
    Value   string `xml:",chardata"`
}

func (m Maps) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    if len(m) == 0 {
        return nil
    }

    err := e.EncodeToken(start)
    if err != nil {
        return err
    }

    for _, ma := range m {
        for k, v := range ma {
            e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})
        }
    }

    return e.EncodeToken(start.End())
}

func (m *Maps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    *m = Maps{}

    for {
        var e xmlMapEntry

        err := d.Decode(&e)
        if err == io.EOF {
            break
        } else if err != nil {
            return err
        }

        *m = append(*m, map[string]string{e.XMLName.Local: e.Value})
    }
    return nil
}

func main() {
    myarraymap := []map[string]string{
        0: {"Key1": "val1"},
        1: {"Key2": "val2"},
        2: {"Randval3": "val1"},
        3: {"Randval2": "xyz1"},
        4: {"Randval1": "xyz2"},
    }

    // Encode to XML
    x, _ := xml.MarshalIndent(Maps(myarraymap), "", "  ")
    fmt.Printf("my marshaled xml map: %v\n", string(x))


    // Decode back from XML
    var rm []map[string]string
    xml.Unmarshal(x, (*Maps)(&rm))
    fmt.Printf("my unmarshalled xml map: %v \n", rm)
}
0
fabem 17 abr. 2020 a las 10:07