user17503953
user17503953

Reputation:

Unmarshal nested map with int keys

If I have a map, I can Marshal it no problem:

package main

import (
   "encoding/json"
   "os"
)

type object map[int]interface{}

func main() {
   obj := object{
      1: "one", 2: object{3: "three"},
   }
   buf, err := json.Marshal(obj)
   if err != nil {
      panic(err)
   }
   os.Stdout.Write(buf) // {"1":"one","2":{"3":"three"}}
}

However I want to do the reverse. I tried this:

package main

import (
   "encoding/json"
   "fmt"
)

func main() {
   buf := []byte(`{"1":"one","2":{"3":"three"}}`)
   var obj map[int]interface{}
   json.Unmarshal(buf, &obj)
   // map[int]interface {}{1:"one", 2:map[string]interface {}{"3":"three"}}
   fmt.Printf("%#v\n", obj)
}

Only the top level has the correct type. Is it possible to do what I am wanting?

Upvotes: 3

Views: 1305

Answers (1)

mkopriva
mkopriva

Reputation: 38343

JSON keys are never anything but strings, that's how the spec is defined, and you can see that in the output of you marshal. So when you try the reverse with interface{} as the top level map's value type, the type information for the nested objects is lost. You'd need a custom map type that implements UnmarshalJSON to be able to do what you want.

For example:

type IntKeyMap map[int]interface{}

func (m *IntKeyMap) UnmarshalJSON(data []byte) error {
    raw := map[int]json.RawMessage{}
    if err := json.Unmarshal(data, &raw); err != nil {
        return err
    }
    for k, v := range raw {
        // check if the value is a nested object
        if len(v) > 0 && v[0] == '{' && v[len(v)-1] == '}' {
            // The following assumes that the nested JSON object's
            // key strings represent integers, if that is not the
            // case this block will fail.
            obj := IntKeyMap{}
            if err := json.Unmarshal([]byte(v), &obj); err != nil {
                return err
            }
            (*m)[k] = obj
        } else {
            var i interface{}
            if err := json.Unmarshal([]byte(v), &i); err != nil {
                return err
            }
            (*m)[k] = i
        }
    }
    return nil
}

https://go.dev/play/p/lmyhqD__Uod

Upvotes: 4

Related Questions