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?
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?
2 Answers
Reset to default 1The 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.
It seems that in the actual code, the Status field of HealthCheck is of a type defined by proto, so the custom MarshalJSON method is not called. You can use type CustomHealthStatus HealthStatus
and convert the Status field that needs to be serialized to CustomHealthStatus. Here is an example:
// CustomHealthStatus wraps HealthStatus for JSON marshaling
type CustomHealthStatus HealthStatus
// MarshalJSON converts a HealthStatus value to a JSON value
func (enum CustomHealthStatus ) MarshalJSON() ([]byte, error) {
return []byte(utils.MarshalString(enum, HealthStatus_name, HealthStatusMapping, utils.DoubleQuotes)), nil
}
func main() {
healthCheck := &HealthCheck{
Version: "v1.0.0",
Status: HealthStatus_HEALTH_STATUS_UP,
}
customHealthCheck := struct {
Version string `json:"version"`
Status CustomHealthStatus `json:"status"`
}{
Version: healthCheck.Version,
Status: CustomHealthStatus(healthCheck.Status),
}
// marshalOptions := protojson.MarshalOptions{
// UseProtoNames: false,
// }
// jsonData, err := marshalOptions.Marshal(proto.Message(healthCheck))
jsonData, err := json.Marshal(customHealthCheck)
if err != nil {
log.Fatalf("Failed to marshal HealthCheck: %v", err)
}
fmt.Printf("Serialized HealthCheck: %s\n", jsonData) // Serialized HealthCheck: {"version":"v1.0.0","status":"up"}
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744206502a4563129.html
评论列表(0条)