JSON encoder does not use custom TextMarshaler for map keys when the map key type is a custom string type

135 views
Skip to first unread message

Barakat Barakat

unread,
Oct 23, 2019, 11:45:24 PM10/23/19
to golang-nuts
I'm using macOS Mojave, go 1.12.4

I would expect the key and value in the example to be the same.

If you implement TextMarshaler on a custom string type, the encoder does not use the marshaler when encoding map keys of that type. I ran into this trying to use bson.ObjectId from the globalsign mgo repo as a key in a map and trying to serialize it to JSON. The JSON encoder uses reflection to check that the 'kind' of the value is a string, which is true. But it does not check if it is a custom string type that has TextMarshaler implemented. The JSON encoder code:

encoding/json/encode.go 865-884
func (w *reflectWithString) resolve() error {
   
if w.v.Kind() == reflect.String {
      w
.s = w.v.String()
     
return nil
   
}
   
if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok {
      buf
, err := tm.MarshalText()
      w
.s = string(buf)
     
return err
   
}
   
switch w.v.Kind() {
   
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
      w
.s = strconv.FormatInt(w.v.Int(), 10)
     
return nil
   
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
      w
.s = strconv.FormatUint(w.v.Uint(), 10)
     
return nil
   
}
   panic
("unexpected map key type")
}

Is this intentional?

Jake Montgomery

unread,
Oct 24, 2019, 11:53:20 AM10/24/19
to golang-nuts
Just a friendly reminder. When sending code to this list, please use a link to the Go playground or use plain text. The colored text with a black background is unreadable. Thanks.

roger peppe

unread,
Oct 26, 2019, 4:10:09 AM10/26/19
to Barakat Barakat, golang-nuts
This is behaving as documented. The docs say "keys of any string type are used directly".

The reason for that behaviour is probably to avoid breaking backward compatibility, because historically encoding/json did not recognise TextMarshaler in map keys.

You could wrap your type in a struct:

   type jsonKey struct {
       MyStringType
   }

and use that as a key instead, which should work, although I haven't tried it.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/6745c4a2-3d52-46f6-ab8f-000d8a1a3f8d%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages