英文:
Protobuf.Any - Unmarshal from a json.RawMessage
问题
我有一些以json.RawMessage
格式存储在数据库中的数据。具体的列是jsonb类型。
我无法找到一种将数据解组为在proto中定义为protobuf.Any的属性的方法,如下所示:
repeated google.protobuf.Any list = 1;
当我尝试使用json.Unmarshal()
从数据库解组数据时,list
是空的。文档中提到了类似以下的内容:
foo := &pb.Foo{...}
any, err := anypb.New(foo)
if err != nil {
...
}
...
foo := &pb.Foo{}
if err := any.UnmarshalTo(foo); err != nil {
...
}
但在这个示例中,foo
的类型是proto.Message
,而我无法转换为json.RawMessage
。
有没有办法可以做到这一点?
英文:
I have data from the DB that is in json.RawMessage
format. The specific column is jsonb.
I can't really find a way to unmarshal the data to a property that on proto is defined as protobuf.Any like so.
repeated google.protobuf.Any list = 1;
When I try to unmarshal the data from the db using json.Unmarshal()
then list
is empty. The documentation mention something like this:
foo := &pb.Foo{...}
any, err := anypb.New(foo)
if err != nil {
...
}
...
foo := &pb.Foo{}
if err := any.UnmarshalTo(foo); err != nil {
...
}
But in this example foo
is of type proto.Message
which I can't convert since I have json.RawMessage
.
Is there any way I can do this?
答案1
得分: 1
首先,你应该了解数据库列中存储了什么。json.RawMessage
简单地定义为 type RawMessage []byte
(参见文档)。它没有足够的信息来回答你的问题。
我将提供一个演示来展示 google.protobuf.Any
的工作原理,这应该能帮助你更好地理解你的问题。
注意事项:
-
Any 用于在消息中嵌入其他类型。因此,在演示中我定义了另外两个消息(
Foo
和Bar
)。Any
消息类型允许您在没有其 .proto 定义的情况下使用消息作为嵌入类型。Any 包含一个任意序列化的消息字节,以及一个作为全局唯一标识符并解析为该消息类型的 URL。 -
实际上,你的问题取决于数据库中存储了什么。请参考
main.go
中的注释。
演示的文件夹结构:
├── go.mod
├── main.go
└── pb
├── demo.pb.go
└── demo.proto
go.mod
:
module github.com/ZekeLu/demo
go 1.19
require (
github.com/golang/protobuf v1.5.2
google.golang.org/protobuf v1.28.1
)
pb/demo.proto
:
syntax = "proto3";
package pb;
import "google/protobuf/any.proto";
option go_package = "github.com/ZekeLu/demo/pb";
message MyMessage {
repeated google.protobuf.Any list = 1;
}
message Foo {
int32 v = 1;
}
message Bar {
string v = 1;
}
main.go
:
package main
import (
"encoding/json"
"fmt"
"google.golang.org/protobuf/types/known/anypb"
"github.com/ZekeLu/demo/pb"
)
func main() {
// 如果数据库存储了 pb.Foo 的实例,则首先进行解组。
buf := json.RawMessage([]byte(`{"v":10}`))
var foo pb.Foo
err := json.Unmarshal(buf, &foo)
if err != nil {
panic(err)
}
// 然后将其编组为新的 Any 实例,该实例可用于创建可分配给 pb.MyMessage.List 的切片。
a1, err := anypb.New(&foo)
if err != nil {
panic(err)
}
bar := &pb.Bar{V: "10"}
a2, err := anypb.New(bar)
if err != nil {
panic(err)
}
// 初始化 List 字段。
m := pb.MyMessage{List: []*anypb.Any{a1, a2}}
buf, err = json.Marshal(&m)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", buf)
// 输出: {"list":[{"type_url":"type.googleapis.com/pb.Foo","value":"CAo="},{"type_url":"type.googleapis.com/pb.Bar","value":"CgIxMA=="}]}
// 如果数据库存储了上述输出,则可以直接解组。
var m2 pb.MyMessage
err = json.Unmarshal(buf, &m2)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", m2.List)
// 输出: [[type.googleapis.com/pb.Foo]:{v:10} [type.googleapis.com/pb.Bar]:{v:"10"}]
}
运行演示的步骤:
$ protoc --proto_path=pb --go_out=pb --go_opt=paths=source_relative demo.proto
$ go mod tidy
$ go run main.go
英文:
First of all, you should understand what is stored in the DB column. json.RawMessage
is simply defined as type RawMessage []byte
(see the doc). And it does not carry enough information to answer your question.
I will provide a demo to show how google.protobuf.Any
works, which should help you understand your question better.
Notes:
- Any is for embedding other types in a message. So I define two other messages (
Foo
andBar
) in the demo.
> TheAny
message type lets you use messages as embedded types without having their .proto definition. An Any contains an arbitrary serialized message as bytes, along with a URL that acts as a globally unique identifier for and resolves to that message's type. - Actually, your question depends on what is stored in the DB. See the comments in
main.go
.
Folder structure of the demo:
├── go.mod
├── main.go
└── pb
├── demo.pb.go
└── demo.proto
go.mod
:
module github.com/ZekeLu/demo
go 1.19
require (
github.com/golang/protobuf v1.5.2
google.golang.org/protobuf v1.28.1
)
pb/demo.proto
:
syntax = "proto3";
package pb;
import "google/protobuf/any.proto";
option go_package = "github.com/ZekeLu/demo/pb";
message MyMessage {
repeated google.protobuf.Any list = 1;
}
message Foo {
int32 v = 1;
}
message Bar {
string v = 1;
}
main.go
:
package main
import (
"encoding/json"
"fmt"
"google.golang.org/protobuf/types/known/anypb"
"github.com/ZekeLu/demo/pb"
)
func main() {
// If the db stores an instance of pb.Foo, then unmarshal it first.
buf := json.RawMessage([]byte(`{"v":10}`))
var foo pb.Foo
err := json.Unmarshal(buf, &foo)
if err != nil {
panic(err)
}
// And then marshal it into a new Any instance, which can be used to
// create a slice that can be assigned to pb.MyMessage.List.
a1, err := anypb.New(&foo)
if err != nil {
panic(err)
}
bar := &pb.Bar{V: "10"}
a2, err := anypb.New(bar)
if err != nil {
panic(err)
}
// Initialize the List field.
m := pb.MyMessage{List: []*anypb.Any{a1, a2}}
buf, err = json.Marshal(&m)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", buf)
// Output: {"list":[{"type_url":"type.googleapis.com/pb.Foo","value":"CAo="},{"type_url":"type.googleapis.com/pb.Bar","value":"CgIxMA=="}]}
// If the db stores the output above, it can be unmarshal directly
var m2 pb.MyMessage
err = json.Unmarshal(buf, &m2)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", m2.List)
// Output: [[type.googleapis.com/pb.Foo]:{v:10} [type.googleapis.com/pb.Bar]:{v:"10"}]
}
Steps to run the demo:
$ protoc --proto_path=pb --go_out=pb --go_opt=paths=source_relative demo.proto
$ go mod tidy
$ go run main.go
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论