找回密码
 立即注册
查看: 272|回复: 0

Protobuf 入门级指南(谷歌的序列化框架,类似json,但更快 ...

[复制链接]
发表于 2022-6-24 15:45 | 显示全部楼层 |阅读模式
最近公司有用到Protobuf这个序列化框架,以前从来没用过,所以今天学习了下,入个门。
    Protobuf 是一种与平台和语言无关的序列化框架,常用于通信协议,数据存储等。
    优点:它的速度比xml和json更快,同时他能将数据压缩的更小,对于结构化数据优势更明显。什么是结构化数据呢?就是带有一定结构的数据。比如电话簿上有很多记录数据,每条记录包含姓名、ID、邮件、电话等,这种结构重复出现。
    缺点:需要你定义proto文件,然后根据proto文件生成对应的java类,然后我们对这个java类进行序列化和反序列化。并且proto文件和生成的java类都比较反人类,难以读懂。

    安装:

    首先需要安装proto到本地
下载地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.7.1
   然后将 bin 路径添加到 path 环境变量里,使用命令protoc --version查看是否安装成功。
    使用方法:
    1. 首先定义一个你proto文件ProtobufTest.proto
syntax = "proto3"; // PB协议版本

import "google/protobuf/any.proto"; // 引用外部的message,可以是本地的,也可以是此处比较特殊的 Any

option java_package = "com.didispace.web"; // 生成类的包名,注意:会在指定路径下按照该包名的定义来生成文件夹
option java_outer_classname="PersonTestProtos"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名

message PersonTest {
    int32 id = 1; // int 类型
    string name = 2; // string 类型
    string email = 3;
    Sex sex = 4; // 枚举类型
    repeated PhoneNumber phone = 5; // 引用下面定义的 PhoneNumber 类型的 message repeated的含义是这里可以放多个PhoneNumber,相当于list
    map<string, string> tags = 6; // map 类型
    repeated google.protobuf.Any details = 7; // 使用 google 的 any 类型

    // 定义一个枚举
    enum Sex {
        DEFAULT = 0;
        MALE = 1;
        Female = 2;
    }

    // 定义一个 message
    message PhoneNumber {
        string number = 1;
        PhoneType type = 2;

        enum PhoneType {
            MOBILE = 0;
            HOME = 1;
            WORK = 2;
        }

    }

}

    2. 在proto文件所在目录用命令行执行命令,生成java实体类PersonTestProtos
protoc -I=./ --java_out=./ ./ProtobufTest.proto
     然后就能看到对应的目录下生成了实体类。




        3. 序列化和反序列对应的PersonTestProtos类代码例子如下,同时下方也有protobuf对象和json互转的例子,因为有时候我们与第三方公司交互时,有可能使用到json。
package com.didispace.web;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.googlecode.protobuf.format.JsonFormat;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
* @ClassName: ProtobufTest
* @Description: ProtoBuf 测试
**/
public class ProtobufTest {

    public static void main(String[] args) {
        try {
            /** Step1:生成 personTest 对象 */
            // personTest 构造器
            PersonTestProtos.PersonTest.Builder personBuilder = PersonTestProtos.PersonTest.newBuilder();
            // personTest 赋值
            personBuilder.setName("yizhiyuan");
            personBuilder.setEmail("test@gmail.com");
            personBuilder.setSex(PersonTestProtos.PersonTest.Sex.MALE);

            // 内部的 PhoneNumber 构造器
            PersonTestProtos.PersonTest.PhoneNumber.Builder phoneNumberBuilder = PersonTestProtos.PersonTest.PhoneNumber.newBuilder();
            // PhoneNumber 赋值
            phoneNumberBuilder.setType(PersonTestProtos.PersonTest.PhoneNumber.PhoneType.MOBILE);
            phoneNumberBuilder.setNumber("17717037257");

            // personTest 设置 PhoneNumber
            personBuilder.addPhone(phoneNumberBuilder);

            // 生成 personTest 对象
            PersonTestProtos.PersonTest personTest = personBuilder.build();

            //查看生成的personTest对象
            System.out.println("生成的personTest对象" + personTest.toString());

            // 粘包,将一个或者多个protobuf 对象字节写入 stream
            // 序列化
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            personTest.writeDelimitedTo(byteArrayOutputStream);

            // Step2:反序列化,从 steam 中读取一个或者多个 protobuf 字节对象
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            PersonTestProtos.PersonTest personTestResult = PersonTestProtos.PersonTest.parseDelimitedFrom(byteArrayInputStream);
            System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s,邮箱:%s",
                    personTestResult.getName(), personTest.getSexValue(), personTest.getPhone(0).getNumber(),
                    personTest.getEmail()));

            // Step3:protobuf 对象与json的相互转换,有时候我们与第三方系统可能要使用json
            // 将personTest 对象转换为json
            Strin json =  JsonFormat.printToString(personTest);
            System.out.println("对象转换为string后:"+JsonFormat.printToString(personTest));

            //将json反序列化为protobuf对象
            PersonTestProtos.PersonTest.Builder personBuilderForJson = PersonTestProtos.PersonTest.newBuilder();
            JsonFormat.merge(json,personBuilderForJson);
            System.out.println("json 反序列化为对象:"+ personBuilderForJson.build().toString());
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}
            
            大家如果有任何问题,欢迎关注"Java自学之路"这个公众号来提问,里面也有自学全套视频(含项目)和Idea激活码等信息。
        最后,用命令生成对应的java文件其实挺麻烦的,protobuf也提供了与maven和gradle集成的插件,通过相关配置,在maven的compile或者在gradle的build的时候,可以生成对应的java文件。大家有兴趣可以看看protobuf-maven-plugin 和 protobuf-gradle-plugin 这两个插件。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-26 07:52 , Processed in 0.067944 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表