protobuf
protobuf copied to clipboard
Unable to marshal Timestamp to RFC3339 date
What version of protobuf and what language are you using?
libprotoc 3.20.0
google.golang.org/protobuf/cmd/protoc-gen-go@latest
go version
go version go1.18 windows/amd64
What did you do?
If possible, provide a recipe for reproducing the error.
A complete runnable program is good with .proto and .go source code.
syntax = "proto3";
package pb;
option go_package = "github.com/fake/fake/pb";
message Test {
google.protobuf.Timestamp ts = 1;
}
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/fake/fake/pb"
"google.golang.org/protobuf/types/known/timestamppb"
)
func main() {
t := time.Date(2022, 4, 13, 14, 54, 5, 323, time.UTC)
want, _ := t.MarshalJSON()
fmt.Printf("want: %s\n", want)
test := pb.Test{Ts: timestamppb.New(t)}
got, _ := json.Marshal(test)
fmt.Printf("got: %s\n", got)
}
What did you expect to see? want: "2022-04-13T14:54:05.000000323Z" got: {"ts":"2022-04-13T14:54:05.000000323Z"}
What did you see instead? want: "2022-04-13T14:54:05.000000323Z" got: {"ts":{"seconds":1649861645,"nanos":323}}
Make sure you include information that can help us debug (full error message, exception listing, stack trace, logs).
Anything else we should know about your project / environment?
https://developers.google.com/protocol-buffers/docs/proto3#json as well as the code in google.golang.org/[email protected]/types/known/timestamppb/tamestamp.pb.go says that ...
In JSON format, the Timestamp type is encoded as a string in the RFC3339
That is, the format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z
This is clearly not happening and it seems to be out of spec.
In issue #466 there is a hit to use https://godoc.org/github.com/golang/protobuf/jsonpb but that project is deprecated and points back here.
Use the google.golang.org/protobuf/encoding/protojson package to marshal protobuf messages as JSON.
Should perhaps be a note about it in the tutorial https://developers.google.com/protocol-buffers/docs/gotutorial
How would you approach this case below, without the answer of protojson to unmarshall into a predefined message but to a simple struct that is part of the message instead?
package main
import (
"encoding/json"
"fmt"
"time"
"google.golang.org/protobuf/runtime/protoimpl"
"google.golang.org/protobuf/types/known/timestamppb"
)
type Test struct {
Id string `json:"id,omitempty"`
ExpiresOn time.Time `json:"expiresOn,omitempty"`
}
type TestPB struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
ExpiresOn *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expiresOn,proto3" json:"expiresOn,omitempty"`
}
func main() {
u := Test{Id: "123", ExpiresOn: time.Now()}
b, err := json.Marshal(u)
if err != nil {
panic (err)
}
var m TestPB
err = json.Unmarshal(b, &m)
if err != nil {
panic (err)
}
fmt.Printf("PB is %v", m)
}
Your question has already been answered in https://github.com/golang/protobuf/issues/1593