JSON marshaling for maps with special key types

I have a data structure which uses maps with unusual key types (structs, but the problem can be illustrated with any type); I want to marshal it to JSON.

$ go version
go version go1.6.2 linux/amd64

JSON does not allow arbitrary objects to be used as map keys. One workaround, according Go’s encoding/json package, is

The map’s key type must either be a string, an integer type, or implement encoding.TextMarshaler.

I tried it the third option:

package main

import (
    "fmt"
    "encoding/json"
)

type Int int

func (i Int) MarshalText() (text []byte, err error) {
  return []byte("int"), nil
}

func main() {
    m := make(map[Int]int)
    m[Int(42)] = -19

    b, err := json.Marshal(m)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(b))
}

and I get

panic: json: unsupported type: map[main.Int]int

I find it strange that the map type as a whole is reported as the problem rather than the type of its key. The encoding/json documentation implies that maps are supported insofar as their keys implement encoding.TextMarshaler.

I have also tried implementing the json.Marshaler interface:

func (i Int) MarshalJSON() (text []byte, err error) {
    return json.Marshal(int(i))
}

which does seem to change anything.

What am I missing?

What version of Go are you using?

With go version go1.8.3 darwin/amd64 I run

package main

import (
    "fmt"
    "encoding/json"
)

type Int int

func (i Int) MarshalText() (text []byte, err error) {
  return []byte("int"), nil
}

func main() {
    m := make(map[Int]int)
    m[Int(42)] = -19

    b, err := json.Marshal(m)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(b))
}

and get:

{"int":-19}
2 Likes

I’ve added my version to the original post.

1 Like

The feature you are referring to was added in Go 1.7. (Which @nathankerr was strongly hinting at by asking about your version and showing that it works in the current stable one. :slight_smile: )

2 Likes

Thanks @calmh and @nathankerr.

If you can’t upgrade your Go version, you can probably vendor the current encoding/json into your project.

My recommendation is, if at all possible, to upgrade Go. https://pocketgophers.com/when-should-you-upgrade-go/

2 Likes

Indeed. Why Ubuntu gives me ancient packages, I don’t know. I’m an Arch user.

This worked for me:

sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt-get update
sudo apt-get install golang-go

After which, my code works.

Having worked for Canonical I can tell you with certainty that their version will always be out of date.

I strongly recommend using the Go binaries distributed from the Go project itself. They are always up to date, extremely well tested, and known to work on a wide array of operating systems.

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.