zengxiaobai2
zengxiaobai2

Reputation: 111

How to decode json strings to sync.Map instead of normal map in Go1.9?

I can decode json strings to a map with go language like this:

func main(){
  date := []byte(`{"127.1":{"host":"host1","list":["list123","list456"]},"127.2":{"host":"host2","list":["list223","list256"]}}`)
  var x interface{}
  json.Unmarshal(date, &x)
  t := x.(map[string]interface{})
  var aa []interface{}
  aa = (t["127.2"].(map[string]interface{})["list"])
  for _, v := range aa {
     fmt.Println(v.(string))
  }
}

but I wonder how to decode it to a sync.Map in Go1.9. I have tried many ways but failed, can anyone help me?

I tried like this:

    func main(){
      date := []byte(`{"127.1":{"host":"host1","list":["list123","list456"]},"127.2":{"host":"host2","list":["list223","list256"]}}`)
      var x interface{}
      json.Unmarshal(date, &x)
      t := x.((sync.Map)[string]interface{})  //compile error
}

Also I tried like this:

    func main(){
      date := []byte(`{"127.1":{"host":"host1","list":["list123","list456"]},"127.2":{"host":"host2","list":["list223","list256"]}}`)
      var x sync.Map
      json.Unmarshal(date, &x)
      fmt.Println(x) // but the map has nothing
}

Upvotes: 6

Views: 3504

Answers (3)

Seraf
Seraf

Reputation: 950

Just adding to @jakub great answer about MarshalJSON, you can also have a generic function that doesn't check the type (since json doesn't care either)

func MarshalJSON(m *sync.Map) ([]byte, error) {
    tmpMap := make(map[interface{}]interface{})
    m.Range(func(k, v interface{}) bool {
        tmpMap[k] = v
        return true
    })
    return json.Marshal(tmpMap)
}

This could take any kind of sync.Map and just Marshal it.

To add to the UnMarshal function from @Flimzy's answer, you can play a bit with the types by omitting the string part to make it more generics (pun intended, cry in corner):

func UnmarshalJSON(data []byte) (*sync.Map, error) {
    var tmpMap map[interface{}]interface{}
    m := &sync.Map{}

    if err := json.Unmarshal(data, &tmpMap); err != nil {
        return m, err
    }

    for key, value := range tmpMap {
        m.Store(key, value)
    }
    return m, nil
}

In general it is good to play with the same Types when doing this sort of manipulation, because we are lazy and we don't want to rewrite functions ;)

Upvotes: 4

Jonathan Hall
Jonathan Hall

Reputation: 79546

You cannot directly unmarshal into a sync.Map, because sync.Map has no exported fields (so the Unmarshaler doesn't have any way to store data in it), and it doesn't implement the json.Unmarshaler interface.

So you'll have to handle this yourself, probably by including a sync.Map in a type of your own, and implementing json.Unmarshaler on that type:

type Foo struct {
    sync.Map
}

func (f *Foo) UnmarshalJSON(data []byte) error {
    var tmpMap map[string]interface{}
    if err := json.Unmarshal(data, &tmpMap); err != nil {
        return err
    }
    for key, value := range tmpMap {
        f.Store(key, value)
    }
    return nil
}

Upvotes: 9

Jakub Sacha
Jakub Sacha

Reputation: 101

In case you need a snippet to do it another way around

func (f Foo) MarshalJSON() ([]byte, error) {
    tmpMap := make(map[YourTypeOfKey]YourTypeOfValue)
    f.Range(func(k, v interface{}) bool {
        tmpMap[k.(YourTypeOfKey)] = v.(YourTypeOfValue)
        return true
    })
    return json.Marshal(tmpMap)
}

Upvotes: 10

Related Questions