I'm writing a service using connectrpc.This protocol allows for connections to the service through either HTTP or gRPC. The gRPC handlers work fine, but for the HTTP handlers, I want to allow for custom JSON marshaling and unmarshling for some enum types. Currently, I have this:
// HealthCheck contains information about the health of a service.
type HealthCheck struct {
state protoimpl.MessageState `protogen:"open.v1"`
// version of the service.
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
// status of the service.
Status HealthStatus `protobuf:"varint,2,opt,name=status,proto3,enum=common.v1.HealthStatus" json:"status,omitempty"`
// dependencies contains a mapping between the name of the dependency and its status.
Dependencies map[string]*DependencyStatus `protobuf:"bytes,3,rep,name=dependencies,proto3" json:"dependencies,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
// HealthStatusAlternates contains alternate values for the HealthStatus enum
var HealthStatusAlternates = map[string]HealthStatus{
"": HealthStatus_HEALTH_STATUS_UNSPECIFIED,
"up": HealthStatus_HEALTH_STATUS_UP,
"down": HealthStatus_HEALTH_STATUS_DOWN,
}
// HealthStatusMapping contains alternate names for the HealthStatus enum
var HealthStatusMapping = map[HealthStatus]string{
HealthStatus_HEALTH_STATUS_UNSPECIFIED: "",
HealthStatus_HEALTH_STATUS_UP: "up",
HealthStatus_HEALTH_STATUS_DOWN: "down",
}
// MarshalJSON converts a HealthStatus value to a JSON value
func (enum HealthStatus) MarshalJSON() ([]byte, error) {
return []byte(utils.MarshalString(enum, HealthStatus_name, HealthStatusMapping, utils.DoubleQuotes)), nil
}
I'm also using protojson to attempt to set this up:
path, handler := registrar(inner,
connectproto.WithJSON(
protojson.MarshalOptions{AllowPartial: true, UseProtoNames: false, EmitUnpopulated: false, EmitDefaultValues: false},
protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true},
),
)
mux := http.NewServeMux()
mux.Handle(path, handler)
h2cHandler := h2c.NewHandler(mux, &http2.Server{})
httpServerExitDone := new(sync.WaitGroup)
httpServerExitDone.Add(1)
srv := startHTTPServer(httpServerExitDone, h2cHandler)
<-ctx.Done()
if err := srv.Shutdown(ctx); err != nil {
panic(err)
}
httpServerExitDone.Wait()
log.Printf("Shutdown of %s (%s) requested.", name, version)
However, when I attempt to call this service I expect the HealthCheck
to serialize as {"version": "v1.0.0", "status": "up"}
and instead I get {"version": "v1.0.0", "status": "HEALTH_STATUS_UP"}
. Debugging into the code, I see that MarshalJSON
isn't being called and protojson
doesn't actually use it either. Is there any way I can enable this behavior?
The ProtoJSON format specifies “enum: The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values.”
This is also how protojson encoding is implemented.
So no, this would be a specification violation. If you want something else, you have to implement a new library.