将值和切片存储在地图中

How can I store values in map which can be both Type and []Type?

I tried this:

mymap := make(map[string]interface{})
mymap["string"] = "word"
mymap["vector"] = append(mymap["vector"].([]interface{}), "earth")

But I have an error:

panic: interface conversion: interface {} is nil, not []interface {}

The purpose of such mymap to use it for json.Marshal to get json object like this:

{
    "string": "word",
    "vector": [
        "earth",
        "sun"
    ]
}

This will work, but you MUST ALWAYS manually check if the item is in the map AND run type assertions and act on them differently. I suggest making a type and only using methods instead of operating on the map itself.

https://play.golang.org/p/e2SFTN70F4v

var ErrNotExists = errors.New("value doesn't exist")
var ErrWrongType = errors.New("variable is wrong type")

type MyMap map[string]interface{}

func(m MyMap) IsArray(key string) bool {
    val, ok := m[key]
    if !ok {
        return false
    }
    _, ok = val.([]string)
    return ok
}

func(m MyMap) Exists(key string) bool {
    _, ok := m[key]
    return ok
}

func(m MyMap) SetString(key, value string) {
    m[key] = value
}

func(m MyMap) SetArray(key string, value []string) {
    m[key] = value
}

func(m MyMap) GetString(key string) (string, error) {
    v, ok := m[key]
    if !ok {
        return "", ErrNotExists
    }
    realVal, ok := v.(string)
    if !ok {
        return "", ErrWrongType
    }
    return realVal, nil
}

func(m MyMap) GetArray(key string) ([]string, error) {
    v, ok := m[key]
    if !ok {
        return nil, ErrNotExists
    }
    realVal, ok := v.([]string)
    if !ok {
        return nil, ErrWrongType
    }
    return realVal, nil
}

func(m MyMap) AppendArray(key string, toAppend []string) error {
    v, ok := m[key]
    if !ok {
        return ErrNotExists
    }
    realVal, ok := v.([]string)
    if !ok {
        return ErrWrongType
    }
    m[key] = append(realVal, toAppend...)
    return nil
}

func main() {
    m := make(MyMap)
    m.SetString("string","hello")
    m.SetArray("array",[]string{"hi","there"})
    fmt.Println(m)
    fmt.Println(m.IsArray("string"), m.IsArray("array"))
    s, err := m.GetString("string")
    if err != nil { // handle
        fmt.Println(err)
    }
    a, err := m.GetArray("array")
    if err != nil { // handle
        fmt.Println(err)
    }
    fmt.Println(s,a)
    m.AppendArray("array", []string{"all","you","people"})
    a, err = m.GetArray("array")
    if err != nil { // handle
        fmt.Println(err)
    }
    fmt.Println(a)
}

You can do something like this by defining JSON encoding processing of the value of map.

https://play.golang.org/p/1IW_hx8clhM

package main

import (
  "encoding/json"
  "fmt"
)

type MyMap map[string]MyValue

func (m MyMap) Put(k, v string) {
  if list, ok := m[k]; ok {
    m[k] = append(list, v)
  } else {
    m[k] = []string{v}
  }
}

type MyValue []string

func (m MyValue) MarshalJSON() ([]byte, error) {
  if len(m) == 1 {
    return json.Marshal(m[0])
  } else {
    return json.Marshal([]string(m))
  }
}

func main() {
  m := make(MyMap)
  m.Put("string", "word")
  m.Put("vector", "earth")
  m.Put("vector", "sun")
  if data, err := json.Marshal(m); err == nil {
    fmt.Println(string(data))
  }
}