Golang中依赖仓库存在vendor目录时Proto序列化与反序列化失败问题
Golang中依赖仓库存在vendor目录时Proto序列化与反序列化失败问题
我有一个 main.go 文件,它使用 pkg/models 中的 proto 文件来 Marshal 和 Unmarshal proto 结构体,如下所示:
// 转换为字符串
protoStr := proto.MarshalTextString(proto)
// 将字符串解组回 proto 结构体
var proto2 models.Stuff
err := proto.UnmarshalText(protoStr, &proto2)
项目设置在此处:https://github.com/chuyval/qqs/tree/master/q2
该项目包含一个 vendor 目录,其中仅检出了 github.com/golang/protobuf 仓库。(如果 vendor 目录不存在,请运行 glide install 来创建)
当在项目内部运行 go run main.go 时,main.go 程序运行正常。
当我把 main.go 文件向上移动一级到父目录,并在父目录级别运行相同的命令 go run main.go 时,会报告以下错误:
line 2: unknown field name "value_list" in models.Stuff
当我删除项目目录中的 vendor 目录,并在父目录级别运行 go run main.go 时,没有出现错误。
为什么在项目仓库中存在 vendor 目录会导致它出错?
示例代码:
package main
import (
"github.com/chuyval/qqs-helper/q2/pkg/models"
"github.com/golang/protobuf/proto"
"log"
)
func main () {
stuff := createProtoStuff()
log.Printf("Stuff: %+v", stuff)
// 转换为字符串
stuffStr := proto.MarshalTextString(stuff)
// 将字符串解组回 proto 结构体
var stuff2 models.Stuff
err := proto.UnmarshalText(stuffStr, &stuff2)
if err != nil {
log.Printf("It didnt work. Error: %s", err.Error())
} else {
log.Printf("It worked. Proto: %+v", stuff2)
}
}
func createProtoStuff() *models.Stuff {
someValueList := []*models.SomeValue{&models.SomeValue{Id: "Some Value ID"}}
valueList := &models.SomeValueList{SomeValue: someValueList}
stuffValueList := &models.Stuff_ValueList{
ValueList: valueList,
}
stuff := &models.Stuff{
Id: "Stuff List Id",
Stuff: stuffValueList,
}
return stuff
}
软件版本
glide version 0.13.1
go version go1.10.3 darwin/amd64
protoc version libprotoc 3.6.0
更多关于Golang中依赖仓库存在vendor目录时Proto序列化与反序列化失败问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中依赖仓库存在vendor目录时Proto序列化与反序列化失败问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go项目中,当存在vendor目录时,Proto序列化与反序列化失败的问题通常是由于依赖包版本冲突导致的。具体来说,当项目目录中存在vendor目录时,Go工具链会优先使用vendor中的依赖包版本,而不是全局GOPATH或模块缓存中的版本。
在你的情况下,vendor目录中的github.com/golang/protobuf版本可能与项目实际需要的版本不兼容,或者vendor中的proto包与生成的Go代码版本不匹配。
问题分析
- vendor目录优先级:当vendor目录存在时,Go编译器会优先使用vendor中的依赖
- 版本不匹配:vendor中的protobuf库版本可能与proto文件生成的Go代码版本不一致
- 类型注册差异:不同版本的protobuf库可能有不同的类型注册机制
解决方案
方案1:更新vendor依赖
确保vendor中的protobuf库版本与proto文件生成时使用的版本一致:
// 重新生成proto文件,确保版本匹配
// protoc --go_out=. path/to/your.proto
// 然后更新vendor
glide update github.com/golang/protobuf
方案2:使用Go Modules替代Glide
迁移到Go Modules可以更好地管理依赖版本:
# 初始化go mod
go mod init your-project
go mod tidy
方案3:检查proto文件定义
确保proto文件中的字段定义正确:
// models.proto
syntax = "proto3";
package models;
message SomeValue {
string id = 1;
}
message SomeValueList {
repeated SomeValue some_value = 1; // 注意字段名
}
message Stuff {
string id = 1;
oneof stuff {
SomeValueList value_list = 2; // 确保字段名匹配
}
}
方案4:在代码中显式注册类型
在某些情况下,需要显式注册proto类型:
package main
import (
"github.com/chuyval/qqs-helper/q2/pkg/models"
"github.com/golang/protobuf/proto"
"log"
)
func init() {
// 确保proto类型被正确注册
_ = &models.Stuff{}
_ = &models.SomeValueList{}
_ = &models.SomeValue{}
}
func main() {
stuff := createProtoStuff()
log.Printf("Stuff: %+v", stuff)
// 使用proto.Marshal替代MarshalTextString以获得更好的兼容性
data, err := proto.Marshal(stuff)
if err != nil {
log.Fatalf("Marshal error: %v", err)
}
// 反序列化
var stuff2 models.Stuff
err = proto.Unmarshal(data, &stuff2)
if err != nil {
log.Printf("Unmarshal error: %s", err.Error())
} else {
log.Printf("Success: %+v", stuff2)
}
}
func createProtoStuff() *models.Stuff {
someValueList := []*models.SomeValue{{Id: "Some Value ID"}}
valueList := &models.SomeValueList{SomeValue: someValueList}
stuff := &models.Stuff{
Id: "Stuff List Id",
Stuff: &models.Stuff_ValueList{
ValueList: valueList,
},
}
return stuff
}
根本原因
当vendor目录存在时,Go编译器使用vendor中的protobuf库,该库可能与proto文件生成时使用的protoc版本不兼容。删除vendor目录后,编译器使用系统全局安装的protobuf库,版本匹配因此工作正常。
建议统一protoc编译器版本和protobuf Go库版本,或者迁移到Go Modules进行依赖管理。

