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

1 回复

更多关于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代码版本不匹配。

问题分析

  1. vendor目录优先级:当vendor目录存在时,Go编译器会优先使用vendor中的依赖
  2. 版本不匹配:vendor中的protobuf库版本可能与proto文件生成的Go代码版本不一致
  3. 类型注册差异:不同版本的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进行依赖管理。

回到顶部