typescriptgoprotocol-buffersprotobuf.jsprotobuf-go

When using maps in protobuf with string keys, do the keys automatically convert from camelCase to snake_case?


Given the following proto, and assuming we aren't looking to make updates unless absolutely necessary:

message MyMessage {
  map<string, string> data = 1
}

On the Go backend, I'm intending on reading data in the following way:

myAttr, err := myMessage.Data["my_attr"]; err != nil {
    // do stuff
}

On our typescript/graphql layer, how should I input this "my_attr" key? Should it be...

myMessage: MyMessage = {
  data: {
    myAttr: "hello",
  },
}

Or should it be

myMessage: MyMessage = {
  data: {
    "my_attr": "hello",
  },
}

And likewise, is there any issue on the Go backend side?


Solution

  • There is no conversion.

    Map keys are typed by the Protocol Buffer definition.

    In your example, the data map's keys and values are string.

    You're confusing the conversion that applies to Message|field names (e.g. data itself) which is converted to Data and would be converted from my_data to MyData in protoc-compiled e.g. Go sources.

    Conversion ensures idiomatic code generation between languages that use different behavior than Protocol Buffers' PascalCased Message and snake_cased field names.

    Whatever value you provide for the keys and values will remain unchanged.

    package main
    
    import (
        "log/slog"
        "os"
    
        pb "path/to/protos"
        "google.golang.org/protobuf/encoding/protojson"
    )
    
    const (
        key string = "my_attr"
    )
    
    func main() {
        myMessage := &pb.MyMessage{
            Data: map[string]string{key: "hello"},
        }
        slog.Info("Output", "MyMessage", myMessage)
    
        myAttr, ok := myMessage.Data[key]
        if ok {
            slog.Info("Output", key, myAttr)
        }
    
        j, err := protojson.Marshal(myMessage)
        if err != nil {
            slog.Error("unable to marshal myMessage", "err", err)
            os.Exit(1)
        }
    
        slog.Info("Output", "JSON", string(j))
    }
    

    Yields: `

    Output MyMessage="data:{key:\"my_attr\"  value:\"hello\"}"
    Output JSON="{\"data\":{\"my_attr\":\"hello\"}}"
    

    NOTE: When accessing map values with a 2-value assignment, the second value is a Boolean (not type error and conventionally ok) indicating whether the key exists in the map.