-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Bug Description
When a proto message with int64_encoding=NUMBER fields is nested inside a response wrapper message, the generated server handler serializes the numbers as strings instead of JSON numbers.
Root Cause
marshalResponse (in *_http_binding.pb.go) checks if the response implements json.Marshaler before deciding to use json.Marshal vs protojson.Marshal:
if marshaler, ok := response.(json.Marshaler); ok {
return marshaler.MarshalJSON() // dispatches to custom method
}
return protojson.Marshal(msg) // bypasses MarshalJSON entirelyprotojson.Marshal uses protobuf reflection — it does NOT call Go interface methods like json.Marshaler. So even though the inner message has a custom MarshalJSON() generated, it is never called.
The generator (encoding.go:collectInt64EncodingMessages) only generates MarshalJSON() for messages that directly contain NUMBER-encoded int64 fields. Wrapper messages that merely reference such messages are not detected.
Steps to Reproduce
message EventEntry {
repeated int64 timestamps = 1 [(sebuf.http.int64_encoding) = INT64_ENCODING_NUMBER];
}
// Wrapper — no direct int64 NUMBER fields
message GetEventsResponse {
EventEntry data = 1;
}
service EventService {
rpc GetEvents(GetEventsRequest) returns (GetEventsResponse) { ... }
}- Generate server with
protoc-gen-go-http - Implement handler returning
GetEventsResponsewith timestamps populated - Call the endpoint
- Expected:
{"data": {"timestamps": [1700000000000, 1700000001000]}} - Actual:
{"data": {"timestamps": ["1700000000000", "1700000001000"]}}
What Should Happen
GetEventsResponse should also receive a generated MarshalJSON() that re-marshals its data field via json.Marshal(x.Data) (which dispatches to EventEntry.MarshalJSON()).
Affected Components
internal/httpgen/encoding.go—collectInt64EncodingMessagesis not transitiveinternal/clientgen/encoding.go— identical gap
Fix
Two-phase collection in generateInt64EncodingFile:
- Existing: detect messages with direct NUMBER fields
- New: detect wrapper messages whose fields are of a type from phase 1
- Generate wrapper
MarshalJSON()/UnmarshalJSON()that re-marshal nested fields viajson.Marshal