protobuf icon indicating copy to clipboard operation
protobuf copied to clipboard

Unable to marshal Timestamp to RFC3339 date

Open kmpm opened this issue 3 years ago • 4 comments

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.

kmpm avatar Apr 13 '22 13:04 kmpm

Use the google.golang.org/protobuf/encoding/protojson package to marshal protobuf messages as JSON.

neild avatar Apr 13 '22 16:04 neild

Should perhaps be a note about it in the tutorial https://developers.google.com/protocol-buffers/docs/gotutorial

kmpm avatar Apr 14 '22 11:04 kmpm

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)
}

go-aegian avatar Feb 16 '24 14:02 go-aegian

Your question has already been answered in https://github.com/golang/protobuf/issues/1593

puellanivis avatar Feb 17 '24 05:02 puellanivis