Skip to content

wangerzi/protoc-test

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Protobuf ProtJSON 演示项目

这是一个完整的 Protobuf + ProtJSON 格式化演示项目,重点展示 google.golang.org/protobuf/encoding/protojson 包的使用方法和各种配置选项。

🚀 快速开始

1. 环境要求

# 确保已安装 Go (1.19+)
go version

# 确保已安装 protoc 编译器
protoc --version

# 安装必要的 protoc 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

2. 项目初始化

# 克隆项目
git clone <your-repo-url>
cd protoc-test

# 安装依赖
go mod tidy

# 生成 protobuf 代码
make proto

3. 运行演示

# 运行完整演示
go run cmd/main.go

# 或者
make proto && cd cmd && go run main.go

📊 预期运行结果

程序将展示 5 个主要演示部分:

1. 标准配置(小驼峰 + uint64转字符串)

{
  "userId": "user-12345",
  "username": "张三", 
  "totalPoints": "9223372036854775807",  // uint64 → 字符串
  "createdAt": "2023-01-15T10:30:00Z",  // snake_case → camelCase
  // ... 更多字段
}

2. 原始字段名配置(snake_case)

{
  "user_id": "user-12345",
  "created_at": "2023-01-15T10:30:00Z",
  "total_points": "9223372036854775807",
  // ... 保持原始 proto 字段命名
}

3. 紧凑格式(省略默认值)

{"userId":"user-12345","username":"张三","totalPoints":"9223372036854775807"...}

4. 枚举数字格式

{
  "status": 1,  // 使用数字而非字符串 "USER_STATUS_ACTIVE"
  // ... 其他字段
}

5. uint64 字段重点演示

{
  "users": [...],
  "totalCount": "999999999999999999",  // 大 uint64 值转为字符串
  // ... 展示各种 uint64 字段的字符串转换
}

🔧 核心配置选项

MarshalOptions 关键配置

&protojson.MarshalOptions{
    UseProtoNames:     false,  // false=小驼峰, true=snake_case
    EmitDefaultValues: true,   // 是否包含默认值
    UseEnumNumbers:    false,  // false=枚举名称, true=枚举数字
    Indent:           "  ",    // JSON 缩进格式
}

UnmarshalOptions 关键配置

&protojson.UnmarshalOptions{
    DiscardUnknown: true,   // 忽略未知字段
    AllowPartial:   false,  // 是否允许部分消息
}

📁 项目结构

protoc-test/
├── protos/
│   └── user.proto          # Protobuf 定义文件
├── gen/go/user/v1/
│   ├── user.pb.go          # 生成的 Go 代码
│   └── user_grpc.pb.go     # 生成的 gRPC 代码
├── cmd/
│   └── main.go             # 演示程序
├── Makefile                # 构建脚本
├── go.mod                  # Go 模块定义
└── README.md               # 项目说明

🔍 关键特性验证

运行程序后,请重点观察:

  • totalPoints 字段显示为字符串格式
  • userId 使用小驼峰命名而非 user_id
  • ✅ 枚举值可在字符串和数字间切换
  • ✅ 错误输入得到合理的错误提示
  • ✅ 未知字段被正确忽略(如果配置了 DiscardUnknown

示例结果:

% go run cmd/main.go
Protobuf ProtJSON 完整演示
重点展示: google.golang.org/protobuf/encoding/protojson
关键特性: 小驼峰命名 + uint64转字符串
============================================================

=== Protobuf ProtJSON 格式化演示 ===

1. 标准配置 (小驼峰命名, uint64转字符串, 包含默认值):
   配置: UseProtoNames=false, EmitDefaultValues=true
{
  "userId":  "user-12345",
  "username":  "张三",
  "email":  "zhangsan@example.com",
  "age":  25,
  "accountBalanceCents":  "150000",
  "loginCount":  42,
  "totalPoints":  "9223372036854775807",
  "height":  175.5,
  "weight":  68.5,
  "isVerified":  true,
  "isPremium":  false,
  "status":  "USER_STATUS_ACTIVE",
  "createdAt":  "2023-01-15T10:30:00Z",
  "lastLogin":  "2024-12-10T14:20:00Z",
  "sessionTimeout":  "1800s",
  "homeAddress":  {
    "street":  "123 Main St",
    "city":  "San Francisco",
    "state":  "CA",
    "zipCode":  "94102",
    "country":  "US"
  },
  "tags":  [
    "vip",
    "early_adopter",
    "beta_tester"
  ],
  "favoriteNumbers":  [
    7,
    13,
    42
  ],
  "addresses":  [
    {
      "street":  "123 Main St",
      "city":  "San Francisco",
      "state":  "CA",
      "zipCode":  "94102",
      "country":  "US"
    },
    {
      "street":  "456 Tech Ave",
      "city":  "Palo Alto",
      "state":  "CA",
      "zipCode":  "94301",
      "country":  "US"
    }
  ],
  "metadata":  {
    "campaign_id":  "summer2024",
    "device_type":  "iPhone",
    "referrer":  "google",
    "source":  "mobile_app"
  },
  "scores":  {
    "english":  88,
    "math":  95,
    "science":  92
  },
  "nickname":  "小张",
  "luckyNumber":  888,
  "acceptsMarketing":  true,
  "profilePicture":  "ZmFrZV9pbWFnZV9kYXRhX2hlcmU=",
  "phone":  "+86-138-0013-8000"
}

2. 原始字段名配置 (snake_case 命名):
   配置: UseProtoNames=true
{
  "user_id":  "user-12345",
  "username":  "张三",
  "email":  "zhangsan@example.com",
  "age":  25,
  "account_balance_cents":  "150000",
  "login_count":  42,
  "total_points":  "9223372036854775807",
  "height":  175.5,
  "weight":  68.5,
  "is_verified":  true,
  "is_premium":  false,
  "status":  "USER_STATUS_ACTIVE",
  "created_at":  "2023-01-15T10:30:00Z",
  "last_login":  "2024-12-10T14:20:00Z",
  "session_timeout":  "1800s",
  "home_address":  {
    "street":  "123 Main St",
    "city":  "San Francisco",
    "state":  "CA",
    "zip_code":  "94102",
    "country":  "US"
  },
  "tags":  [
    "vip",
    "early_adopter",
    "beta_tester"
  ],
  "favorite_numbers":  [
    7,
    13,
    42
  ],
  "addresses":  [
    {
      "street":  "123 Main St",
      "city":  "San Francisco",
      "state":  "CA",
      "zip_code":  "94102",
      "country":  "US"
    },
    {
      "street":  "456 Tech Ave",
      "city":  "Palo Alto",
      "state":  "CA",
      "zip_code":  "94301",
      "country":  "US"
    }
  ],
  "metadata":  {
    "campaign_id":  "summer2024",
    "device_type":  "iPhone",
    "referrer":  "google",
    "source":  "mobile_app"
  },
  "scores":  {
    "english":  88,
    "math":  95,
    "science":  92
  },
  "nickname":  "小张",
  "lucky_number":  888,
  "accepts_marketing":  true,
  "profile_picture":  "ZmFrZV9pbWFnZV9kYXRhX2hlcmU=",
  "phone":  "+86-138-0013-8000"
}

3. 紧凑格式 (省略默认值, 无缩进):
   配置: EmitDefaultValues=false, Indent=""
{"userId":"user-12345", "username":"张三", "email":"zhangsan@example.com", "age":25, "accountBalanceCents":"150000", "loginCount":42, "totalPoints":"9223372036854775807", "height":175.5, "weight":68.5, "isVerified":true, "status":"USER_STATUS_ACTIVE", "createdAt":"2023-01-15T10:30:00Z", "lastLogin":"2024-12-10T14:20:00Z", "sessionTimeout":"1800s", "homeAddress":{"street":"123 Main St", "city":"San Francisco", "state":"CA", "zipCode":"94102", "country":"US"}, "tags":["vip", "early_adopter", "beta_tester"], "favoriteNumbers":[7, 13, 42], "addresses":[{"street":"123 Main St", "city":"San Francisco", "state":"CA", "zipCode":"94102", "country":"US"}, {"street":"456 Tech Ave", "city":"Palo Alto", "state":"CA", "zipCode":"94301", "country":"US"}], "metadata":{"campaign_id":"summer2024", "device_type":"iPhone", "referrer":"google", "source":"mobile_app"}, "scores":{"english":88, "math":95, "science":92}, "nickname":"小张", "luckyNumber":888, "acceptsMarketing":true, "profilePicture":"ZmFrZV9pbWFnZV9kYXRhX2hlcmU=", "phone":"+86-138-0013-8000"}

4. 枚举数字格式:
   配置: UseEnumNumbers=true
{
  "userId":  "user-12345",
  "username":  "张三",
  "email":  "zhangsan@example.com",
  "age":  25,
  "accountBalanceCents":  "150000",
  "loginCount":  42,
  "totalPoints":  "9223372036854775807",
  "height":  175.5,
  "weight":  68.5,
  "isVerified":  true,
  "isPremium":  false,
  "status":  1,
  "createdAt":  "2023-01-15T10:30:00Z",
  "lastLogin":  "2024-12-10T14:20:00Z",
  "sessionTimeout":  "1800s",
  "homeAddress":  {
    "street":  "123 Main St",
    "city":  "San Francisco",
    "state":  "CA",
    "zipCode":  "94102",
    "country":  "US"
  },
  "tags":  [
    "vip",
    "early_adopter",
    "beta_tester"
  ],
  "favoriteNumbers":  [
    7,
    13,
    42
  ],
  "addresses":  [
    {
      "street":  "123 Main St",
      "city":  "San Francisco",
      "state":  "CA",
      "zipCode":  "94102",
      "country":  "US"
    },
    {
      "street":  "456 Tech Ave",
      "city":  "Palo Alto",
      "state":  "CA",
      "zipCode":  "94301",
      "country":  "US"
    }
  ],
  "metadata":  {
    "campaign_id":  "summer2024",
    "device_type":  "iPhone",
    "referrer":  "google",
    "source":  "mobile_app"
  },
  "scores":  {
    "english":  88,
    "math":  95,
    "science":  92
  },
  "nickname":  "小张",
  "luckyNumber":  888,
  "acceptsMarketing":  true,
  "profilePicture":  "ZmFrZV9pbWFnZV9kYXRhX2hlcmU=",
  "phone":  "+86-138-0013-8000"
}

5. 重点演示:uint64 字段自动转为字符串
   注意观察 totalPoints 和 totalCount 字段:
{
  "users":  [
    {
      "userId":  "user-12345",
      "username":  "张三",
      "email":  "zhangsan@example.com",
      "age":  25,
      "accountBalanceCents":  "150000",
      "loginCount":  42,
      "totalPoints":  "9223372036854775807",
      "height":  175.5,
      "weight":  68.5,
      "isVerified":  true,
      "isPremium":  false,
      "status":  "USER_STATUS_ACTIVE",
      "createdAt":  "2023-01-15T10:30:00Z",
      "lastLogin":  "2024-12-10T14:20:00Z",
      "sessionTimeout":  "1800s",
      "homeAddress":  {
        "street":  "123 Main St",
        "city":  "San Francisco",
        "state":  "CA",
        "zipCode":  "94102",
        "country":  "US"
      },
      "tags":  [
        "vip",
        "early_adopter",
        "beta_tester"
      ],
      "favoriteNumbers":  [
        7,
        13,
        42
      ],
      "addresses":  [
        {
          "street":  "123 Main St",
          "city":  "San Francisco",
          "state":  "CA",
          "zipCode":  "94102",
          "country":  "US"
        },
        {
          "street":  "456 Tech Ave",
          "city":  "Palo Alto",
          "state":  "CA",
          "zipCode":  "94301",
          "country":  "US"
        }
      ],
      "metadata":  {
        "campaign_id":  "summer2024",
        "device_type":  "iPhone",
        "referrer":  "google",
        "source":  "mobile_app"
      },
      "scores":  {
        "english":  88,
        "math":  95,
        "science":  92
      },
      "nickname":  "小张",
      "luckyNumber":  888,
      "acceptsMarketing":  true,
      "profilePicture":  "ZmFrZV9pbWFnZV9kYXRhX2hlcmU=",
      "phone":  "+86-138-0013-8000"
    },
    {
      "userId":  "user-67890",
      "username":  "李四",
      "email":  "lisi@example.com",
      "age":  30,
      "accountBalanceCents":  "0",
      "loginCount":  0,
      "totalPoints":  "18446744073709551615",
      "height":  0,
      "weight":  0,
      "isVerified":  false,
      "isPremium":  false,
      "status":  "USER_STATUS_INACTIVE",
      "createdAt":  "2023-06-20T15:45:00Z",
      "tags":  [
        "regular_user"
      ],
      "favoriteNumbers":  [],
      "addresses":  [],
      "metadata":  {},
      "scores":  {},
      "profilePicture":  "",
      "wechat":  "lisi_wechat"
    }
  ],
  "totalCount":  "999999999999999999",
  "nextPageToken":  "next_page_token_12345"
}

=== Protobuf ProtJSON 反序列化演示 ===

1. 输入的 JSON 数据:
{
  "userId": "user-99999",
  "username": "王五",
  "email": "wangwu@example.com",
  "age": 28,
  "totalPoints": "9223372036854775807",
  "isVerified": true,
  "status": "USER_STATUS_ACTIVE",
  "createdAt": "2023-03-15T08:30:00Z",
  "homeAddress": {
    "street": "789 Demo Road",
    "city": "Beijing",
    "country": "CN"
  },
  "tags": ["test", "demo"],
  "metadata": {
    "source": "web",
    "lang": "zh-CN"
  },
  "phone": "+86-139-0013-9000"
}

2. 反序列化成功! 解析后的 Go 结构体:
   用户ID: user-99999
   用户名: 王五
   邮箱: wangwu@example.com
   年龄: 28
   总积分: 9223372036854775807 (uint64)
   状态: USER_STATUS_ACTIVE
   地址: Beijing, CN
   标签: [test demo]
   联系方式: 电话 +86-139-0013-9000

3. 再次序列化验证数据完整性:
{
  "userId":  "user-99999",
  "username":  "王五",
  "email":  "wangwu@example.com",
  "age":  28,
  "accountBalanceCents":  "0",
  "loginCount":  0,
  "totalPoints":  "9223372036854775807",
  "height":  0,
  "weight":  0,
  "isVerified":  true,
  "isPremium":  false,
  "status":  "USER_STATUS_ACTIVE",
  "createdAt":  "2023-03-15T08:30:00Z",
  "homeAddress":  {
    "street":  "789 Demo Road",
    "city":  "Beijing",
    "state":  "",
    "zipCode":  "",
    "country":  "CN"
  },
  "tags":  [
    "test",
    "demo"
  ],
  "favoriteNumbers":  [],
  "addresses":  [],
  "metadata":  {
    "lang":  "zh-CN",
    "source":  "web"
  },
  "scores":  {},
  "profilePicture":  "",
  "phone":  "+86-139-0013-9000"
}

=== 错误处理和边界情况演示 ===

1. 处理无效 JSON:
   预期错误: proto: (line 1:27): invalid value for int32 field age: "not_a_number"

2. 处理未知字段(配置了 DiscardUnknown=true):
   成功解析,未知字段被忽略
   用户ID: test-user, 用户名: TestUser

3. uint64 边界值处理:
   输入: {"totalPoints": "18446744073709551615"}
   成功: totalPoints = 18446744073709551615

演示完成! 关键要点总结:
✓ protojson.MarshalOptions 提供丰富的配置选项
✓ UseProtoNames=false 实现小驼峰命名 (userId vs user_id)
✓ uint64 字段自动转换为 JSON 字符串,避免精度丢失
✓ 支持所有 protobuf 数据类型,包括嵌套、重复、映射等
✓ 提供完善的错误处理和边界情况支持

About

Protobuf 所有类型的声明、序列化和反序列化的测试

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors