我在Golang中编写RESTful API,它也有一个gRPC api。 API连接到MongoDB数据库,并使用结构来映射实体。我也有一个.proto定义,它匹配像我用于MongoDB的结构一样。
我只是想知道是否有办法分享或重新使用.proto定义的MongoDB调用代码。我注意到strucs protoc生成的每个字段都有json标签,但显然没有bson
标签等。
我有类似......
// Menu -
type Menu struct {
ID bson.ObjectId `json:"id" bson"_id"`
Name string `json:"name" bson:"name"`
Description string `json:"description" bson:"description"`
Mixers []mixers.Mixer `json:"mixers" bson:"mixers"`
Sections []sections.Section `json:"sections" bson:"sections"`
}
但是我还有protoc生成的代码......
type Menu struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
Mixers []*Mixer `protobuf:"bytes,4,rep,name=mixers" json:"mixers,omitempty"`
Sections []*Section `protobuf:"bytes,5,rep,name=sections" json:"sections,omitempty"`
}
目前我不得不根据我所做的事情在两种结构之间进行转换。这很乏味,而且我可能会受到相当大的性能影响。那么是否有更好的方法在两者之间进行转换,或者为两个任务重新使用其中一个?
答案 0 :(得分:1)
我玩过它,并有一个可行的示例:
github.com/gogo/protobuf v1.3.1
go.mongodb.org/mongo-driver v1.4.0
google.golang.org/grpc v1.31.0
首先,我要共享我的proto/contract/example.proto
文件:
syntax = "proto2";
package protobson;
import "gogoproto/gogo.proto";
option (gogoproto.sizer_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;
option go_package = "gitlab.com/8bitlife/proto/go/protobson";
service Service {
rpc SayHi(Hi) returns (Hi) {}
}
message Hi {
required bytes id = 1 [(gogoproto.customtype) = "gitlab.com/8bitlife/protobson/custom.BSONObjectID", (gogoproto.nullable) = false, (gogoproto.moretags) = "bson:\"_id\""] ;
required int64 limit = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "bson:\"limit\""] ;
}
它包含一个简单的gRPC服务Service
,该服务具有SayHi
方法,请求类型为Hi
。它包括一组选项:gogoproto.sizer_all
,gogoproto.marshaler_all
,gogoproto.unmarshaler_all
。您可以在extensions page中找到它们的含义。 Hi
本身包含两个字段:
id
,其中指定了其他选项:gogoproto.customtype
和gogoproto.moretags
limit
仅具有gogoproto.moretags
选项BSONObjectID
的{{1}}字段中使用的 gogoproto.customtype
是一种自定义类型,我定义为id
:
custom/objectid.go
这是必需的,因为我们需要为协议缓冲区和mongodb驱动程序两者定义自定义封送处理和非封送处理方法。这使我们可以将此类型用作mongodb中的对象标识符。并将其“解释”给mongodb驱动程序,我通过在原始文件中使用package custom
import (
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type BSONObjectID primitive.ObjectID
func (u BSONObjectID) Marshal() ([]byte, error) {
return u[:], nil
}
func (u BSONObjectID) MarshalTo(data []byte) (int, error) {
return copy(data, (u)[:]), nil
}
func (u *BSONObjectID) Unmarshal(d []byte) error {
copy((*u)[:], d)
return nil
}
func (u *BSONObjectID) Size() int {
return len(*u)
}
func (u *BSONObjectID) UnmarshalBSONValue(t bsontype.Type, d []byte) error {
copy(u[:], d)
return nil
}
func (u BSONObjectID) MarshalBSONValue() (bsontype.Type, []byte, error) {
return bsontype.ObjectID, u[:], nil
}
选项将其标记为bson
标签。
要从我使用的原始文件生成源代码:
(gogoproto.moretags) = "bson:\"_id\""
我已经在运行MongoDB实例的MacOS上对其进行了测试:protoc \
--plugin=/Users/pstrokov/go/bin/protoc-gen-gogo \
--plugin=/Users/pstrokov/go/bin/protoc-gen-go \
-I=/Users/pstrokov/Workspace/protobson/proto/contract \
-I=/Users/pstrokov/go/pkg/mod/github.com/gogo/protobuf@v1.3.1 \
--gogo_out=plugins=grpc:. \
example.proto
:
docker run --name mongo -d -p 27017:27017 mongo
对不起,最后一个代码片段的格式:) 希望对您有所帮助。
答案 1 :(得分:0)
遇到了同样的问题,有两种解决方法。它们分为两种通用方法:
如果要使用相同的数据类型,则必须修改代码生成
您可以使用类似gogoprotobuf的扩展名来添加标签。这应该在您的结构中为您提供bson
标记。
您还可以使用正则表达式或更复杂的涉及go abstract语法树的方式对生成的文件进行后处理。
如果您选择在它们之间进行映射:
使用reflection
。您可以编写一个包含两个结构的包,然后尝试从一个结构中获取值并将其应用于另一个。您将不得不处理边缘情况(轻微的命名差异,等价的类型等),但是如果出现边缘情况,您将有更好的控制。
使用JSON作为中介。只要生成的json标签匹配,这将是一种快速的编码练习,并且如果代码中的循环不是很紧密,则序列化和反序列化的性能影响是可以接受的。
手写或代码生成映射功能。根据您拥有的结构数,您可以写出一堆在两者之间转换的函数。
在我的工作场所中,我们最终做了所有这些事情:分叉protoc生成器做一些自定义标签,基于反射的结构覆盖包以在任意结构之间进行映射,以及一些对性能更敏感的手写体或更少的自动化映射。
答案 2 :(得分:0)
我正在测试中,可能会在不久后提供代码(如果看不到并想要,请给我发短信),但是https://godoc.org/go.mongodb.org/mongo-driver/bson/bsoncodec看起来像票。 protoc可以创建您的结构,而您不必为自定义它们而烦恼。然后,您可以自定义mongo-driver来为您进行某些类型的映射,看起来它们的库对此相当不错。
这很棒,因为如果我使用原语结构,那么我希望在我的应用程序核心/域层使用它。我不想在那里担心mongoDB的兼容性。
现在,在我看来@Liyan Chang的回答是
如果要使用相同的数据类型,则必须修改代码生成 不一定是这种情况。因为您可以选择使用1个数据类型。
就使用此编解码器系统获取和设置数据到DB而言,您似乎可以使用一种生成的类型和帐户来满足您的需要。
请参见https://stackoverflow.com/a/59561699/8546258-bson struct标记不是全部。看起来编解码器可以完全帮助您解决问题。
请参阅https://stackoverflow.com/a/58985629/8546258,以全面了解编解码器。
请记住,这些编解码器是在mongodb go驱动程序的1.3版中发布的。我发现这将我引向了那里:https://developer.mongodb.com/community/forums/t/mgo-setbson-to-mongo-golang-driver/2340/2?u=yehuda_makarov
答案 3 :(得分:-1)
请看这个回购。 https://github.com/frankee/protobuf(仍然需要gogoprotobuf合并)。
请阅读document中的More Serialization Formats
部分。