golang / protobuf

Go support for Google's protocol buffers
BSD 3-Clause "New" or "Revised" License
9.79k stars 1.58k forks source link

Unable to marshal Timestamp to RFC3339 date #1433

Open kmpm opened 2 years ago

kmpm commented 2 years ago

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/protobuf@v1.28.0/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.

neild commented 2 years ago

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

kmpm commented 2 years ago

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

go-aegian commented 9 months ago

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)
}
puellanivis commented 9 months ago

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