You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Protobuf生成不含额外隐藏字段的Golang结构体?

问题描述

参考以下消息定义:

message Moo {
  uint64 generation = 1;
  string title = 2;
  bytes payload = 3;
  repeated string flags = 4;
}

protoc生成的type Moo struct中包含state、sizeCache、unknownFields等额外字段,我还需要生成一个不含这些额外字段的结构体,例如:

type MooLight struct {
    Generation uint64
    Title      string
    Payload    []byte
    Flags      []string
}

(结构体标签有无对我无关紧要,为清晰起见此处已省略)。

我知道可以编写代码读取生成的moo.pb.go文件,基于var File_moo_proto protoreflect.FileDescriptor生成这类“轻量”结构体,但更希望通过protoc直接实现。请问有什么建议?我曾查看Go protoc插件,但代码量庞大。


可行解决方案

1. 编写极简自定义protoc Go插件

不用照搬官方插件的复杂逻辑,基于google.golang.org/protobuf的protoreflect和protoc-gen-go/plugin包,可以写一个几十行的极简插件,只负责生成轻量结构体:

核心思路:

  • 接收protoc传递的CodeGeneratorRequest
  • 遍历所有消息类型
  • 对每个消息,生成对应的XXXLight结构体,只保留业务字段

示例核心代码片段:

package main

import (
	"google.golang.org/protobuf/compiler/protogen"
	"google.golang.org/protobuf/types/descriptorpb"
)

func main() {
	protogen.Options{}.Run(func(gen *protogen.Plugin) error {
		for _, f := range gen.Files {
			if !f.Generate {
				continue
			}
			generateLightStructs(gen, f)
		}
		return nil
	})
}

func generateLightStructs(gen *protogen.Plugin, file *protogen.File) {
	g := gen.NewGeneratedFile(file.GeneratedFilenamePrefix+"_light.pb.go", file.GoImportPath)
	g.P("// Code generated by protoc-gen-go-light. DO NOT EDIT.")
	g.P()
	g.P("package ", file.GoPackageName)
	g.P()

	for _, msg := range file.Messages {
		structName := msg.GoName + "Light"
		g.P("type ", structName, " struct {")
		for _, field := range msg.Fields {
			fieldType := field.Desc.Kind().String()
			if field.Desc.IsRepeated() {
				fieldType = "[]" + fieldType
			}
			if field.Desc.Kind() == descriptorpb.FieldDescriptorProto_TYPE_BYTES {
				fieldType = "[]byte"
			}
			g.P("\t", field.GoName, " ", fieldType)
		}
		g.P("}")
		g.P()
	}
}

编译这个插件后,用protoc调用:

protoc --go_out=. --go_opt=paths=source_relative --plugin=protoc-gen-go-light=./protoc-gen-go-light moo.proto

2. 使用第三方轻量生成插件

已有成熟的第三方插件可以直接生成无额外字段的结构体,比如:

  • protoc-gen-go-strict: 生成仅包含业务字段的结构体,支持自定义命名规则
  • protoc-gen-go-minimal: 专注生成极简结构体,去掉所有protoc附加字段

使用时只需安装对应插件,然后在protoc命令中添加对应参数即可,无需自己写代码。

3. 结合Descriptor和Go模板生成

如果不想写插件,可以用protoc输出descriptor文件,再结合Go模板生成轻量结构体:

  1. 生成descriptor文件:
protoc --descriptor_set_out=moo.desc --include_imports moo.proto
# 转成JSON格式更易处理
protoc --descriptor_set_out=moo.desc --include_imports moo.proto && protoc --decode_raw < moo.desc > moo.json
  1. 编写Go模板文件(比如light_struct.tmpl):
package {{.Package}}

{{range .Messages}}
type {{.Name}}Light struct {
{{range .Fields}}
	{{.GoName}} {{.Type}}
{{end}}
}
{{end}}
  1. 编写简单的Go程序读取descriptor,解析出字段名称、类型等信息,渲染模板生成代码。

这种方式无需涉及protoc插件的复杂逻辑,适合快速实现需求。


内容的提问来源于stack exchange,提问作者JayEye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 18:50:32