Description
Original discussion at https://groups.google.com/forum/#!searchin/golang-dev/json$20map/golang-dev/5gSHNrJQpUI/vZGSGRmUrC0J
Currently, json.Marshal will fail to marshal Go maps that have non-string keys, e.g.:
// http://play.golang.org/p/2m9wLZATqw
type Coord struct { X,Y int }
occupied := map[Coord]bool{}
occupied[Coord{1,2}] = true
data, err := json.Marshal(occupied)
fmt.Printf("Data: %s\nErr: %v", data, err)
I propose to enhance the encoding/json package such that:
- for
json.Marshal
- If the map key is a string kind it is used directly.
- Otherwise if the map key satisfies the encoding.TextMarshaler interface then that is used to generate the map key.
- Otherwise it fails as it does today.
- for
json.Unmarshal
- If the map key is a string kind it is written directly.
- Otherwise if the map key satisfies the encoding.TextUnmarshaler interface then that is used to decode the map key.
- Otherwise it fails as it does today.
Example
(http://play.golang.org/p/VxhFluFKTX)
This would, for example, allow a map[Coord]bool
to be Marshaled & Unmarshaled if Coord
was defined as:
type Coord struct{ X, Y int }
func (c Coord) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("%d,%d", c.X, c.Y)), nil
}
func (c *Coord) UnmarshalText(p []byte) error {
if n, err := fmt.Sscanf(string(p), "%d,%d", &c.X, &c.Y); err != nil {
return err
} else if n != 2 {
return errors.New("Cannot parse coord: " + string(p))
}
return nil
}
And the Go struct
map[Coord]bool{Coord{1, 2}: true, Coord{3, 4}: true}
would correspond to
{"1,2": true,"3,4":true}
Considerations
If the struct marshals to a non-unique value, e.g.
func (c Coord) MarshalText() ([]byte, error) {
return []byte("always the same"), nil
}
The the json encoder would output JSON that has repeated keys, e.g.:
{"always the same": true,"always the same":true}
This is valid JSON.
Similarly, when decoding a map, it would unmarshal each key and value and then assign that into the map, so last-write-wins, as is currently done.
An interesting side effect is that it would then be possible to explicitly handle JSON with repeated keys (or even explicitly record the order of the json keys) by having the go-side map key TextUnmarshaler have non-deterministic unmarshaling (e.g. record timestamp for each unmarshal).