从bson结构转换protoc生成的结构的最佳方法是什么?

时间:2017-07-17 22:49:02

标签: mongodb go grpc mgo

我在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"`
}

目前我不得不根据我所做的事情在两种结构之间进行转换。这很乏味,而且我可能会受到相当大的性能影响。那么是否有更好的方法在两者之间进行转换,或者为两个任务重新使用其中一个?

4 个答案:

答案 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_allgogoproto.marshaler_allgogoproto.unmarshaler_all。您可以在extensions page中找到它们的含义。 Hi本身包含两个字段:

  1. id,其中指定了其他选项:gogoproto.customtypegogoproto.moretags
  2. 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)

遇到了同样的问题,有两种解决方法。它们分为两种通用方法:

  1. 使用相同的数据类型
  2. 使用两种不同的结构类型并在它们之间进行映射

如果要使用相同的数据类型,则必须修改代码生成

您可以使用类似gogoprotobuf的扩展名来添加标签。这应该在您的结构中为您提供bson标记。

您还可以使用正则表达式或更复杂的涉及go abstract语法树的方式对生成的文件进行后处理。

如果您选择在它们之间进行映射:

  1. 使用reflection。您可以编写一个包含两个结构的包,然后尝试从一个结构中获取值并将其应用于另一个。您将不得不处理边缘情况(轻微的命名差异,等价的类型等),但是如果出现边缘情况,您将有更好的控制。

  2. 使用JSON作为中介。只要生成的json标签匹配,这将是一种快速的编码练习,并且如果代码中的循环不是很紧密,则序列化和反序列化的性能影响是可以接受的。

  3. 手写或代码生成映射功能。根据您拥有的结构数,您可以写出一堆在两者之间转换的函数。

在我的工作场所中,我们最终做了所有这些事情:分叉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部分。