admin管理员组文章数量:1401247
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"}
}
本文标签: goUsing custom encodingjson Marshaler and Unmarshaler interface with protojsonStack Overflow
版权声明:本文标题:go - Using custom encodingjson Marshaler and Unmarshaler interface with protojson - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744206501a2595214.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论