为了账号安全,请及时绑定邮箱和手机立即绑定

Protobuf序列化和反序列化的简单教程

标签:
Python C++ Go
概述

本文详细介绍了Protobuf序列化和反序列化的概念、安装配置方法以及基础语法,通过示例代码展示了如何在Java、C++、Python和Go中进行序列化和反序列化操作。文章还涵盖了常见问题及其解决方法,并推荐了进一步学习的资源。全文旨在帮助读者全面理解并掌握Protobuf序列化和反序列化。

Protobuf序列化和反序列化的简单教程
Protobuf序列化和反序列化的概念介绍

什么是Protobuf

Protocol Buffers(简称Protobuf)是由Google开发的一种语言无关、平台无关、可扩展的序列化结构化数据的机制。这种机制可以在不同的数据结构之间保持数据的兼容性,并且能够高效、紧凑地对数据进行编码和解码。Protobuf 的设计目的是替代 XML 的复杂性和 JSON 的冗长性,尤其是在数据传输和存储方面。

序列化和反序列化的定义

序列化(Serialization)是指将对象的状态信息转换为可以存储或传输的形式的过程。通过序列化,对象可以被持久化到文件、数据库或者通过网络发送到其他程序或机器。反序列化(Deserialization)则是与其相反的过程,即从序列化后的数据恢复成原始对象。

Protobuf序列化和反序列化的安装与配置

下载安装Protobuf环境

  1. 从Protobuf的GitHub仓库下载最新的稳定版本安装包。访问 Protobuf GitHub仓库 下载安装包。
  2. 解压下载的安装包。例如,如果你使用的是Linux操作系统,可以使用如下命令解压:

    tar -xzf protobuf-3.17.1.tar.gz
    cd protobuf-3.17.1
  3. 编译并安装Protobuf。使用./configure命令进行配置,然后使用makemake install命令进行编译和安装。

    ./configure
    make
    sudo make install

配置开发环境

  1. 配置环境变量。将protobuf的库路径和头文件路径添加到系统环境变量中。例如,对于Linux系统,可以将以下环境变量添加到~/.bashrc~/.zshrc文件中:

    export PATH=$PATH:/usr/local/bin
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
    export CPATH=$CPATH:/usr/local/include
  2. 验证安装是否成功。可以通过命令行运行protoc --version来检查是否成功安装了正确的版本:

    protoc --version
Protobuf序列化和反序列化的基础语法

定义消息格式

使用.proto文件定义数据结构。下面是一个简单的.proto文件示例,定义了一个名为Person的消息格式:

syntax = "proto3";

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
}

编译.proto文件

使用protoc命令将.proto文件编译成所需的编程语言的定义文件。例如,将上面的Person.proto文件编译成Java代码。

protoc --java_out=. Person.proto

这将生成一个名为Person.java的Java文件,该文件包含了Person消息的定义。

生成其他语言的代码

除了Java,protoc支持多种语言,包括C++, Python, Go等。下面是一些示例:

  • 编译成C++代码:

    protoc --cpp_out=. Person.proto
  • 编译成Python代码:

    protoc --python_out=. Person.proto
  • 编译成Go代码:

    protoc --go_out=. Person.proto
Protobuf序列化和反序列化示例代码

序列化示例

下面的示例展示了如何使用Java将一个Person对象序列化为二进制数据。

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import java.io.IOException;

public class ProtobufSerializationExample {
    public static void main(String[] args) throws IOException {
        // 创建一个Person对象
        person.Person person = person.Person.newBuilder()
                .setId(1234)
                .setName("John Doe")
                .setEmail("john.doe@example.com")
                .build();

        // 消息对象序列化为二进制数据
        byte[] bytes = person.toByteArray();
        System.out.println("Serialized: " + new String(bytes));

        // 消息对象转换为JSON格式
        String json = JsonFormat.printer().print(person);
        System.out.println("Serialized JSON: " + json);
    }
}

反序列化示例

下面的示例展示了如何使用Java从二进制数据反序列化回Person对象。

import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;

public class ProtobufDeserializationExample {
    public static void main(String[] args) throws IOException {
        // 二进制数据反序列化为Person对象
        byte[] bytes = new byte[]{
                22, 48, 4, 0, 0, 0, 0, 0, 106, 111, 106, 110, 44, 32, 68, 111, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 111, 114, 107, 46, 100,
                111, 101, 100, 64, 101, 97, 115, 116, 114, 111, 117, 114, 112, 101, 108, 46, 99, 111, 109
        };
        person.Person person = person.Person.parseFrom(bytes);

        System.out.println("Deserialized: " + person);
    }
}

C++序列化示例

下面的示例展示了如何使用C++将一个Person对象序列化为二进制数据。

#include "person.pb.h"
#include <iostream>
#include <string>

int main() {
    person::Person person;
    person.set_id(1234);
    person.set_name("John Doe");
    person.set_email("john.doe@example.com");

    // 消息对象序列化为二进制数据
    std::string serialized;
    person.SerializeToString(&serialized);
    std::cout << "Serialized: " << serialized << std::endl;

    return 0;
}

C++反序列化示例

下面的示例展示了如何使用C++从二进制数据反序列化回Person对象。

#include "person.pb.h"
#include <iostream>
#include <string>

int main() {
    std::string serialized = "...\n";
    person::Person person;
    person.ParseFromString(serialized);

    std::cout << "Deserialized: " << person.id() << " " << person.name() << " " << person.email() << std::endl;

    return 0;
}

Python序列化示例

下面的示例展示了如何使用Python将一个Person对象序列化为二进制数据。

from person_pb2 import Person
import json

def main():
    person = Person()
    person.id = 1234
    person.name = "John Doe"
    person.email = "john.doe@example.com"

    # 消息对象序列化为二进制数据
    serialized = person.SerializeToString()
    print(f"Serialized: {serialized}")

    # 消息对象转换为JSON格式
    print(f"Serialized JSON: {json.loads(person.ToJsonString())}")

if __name__ == "__main__":
    main()

Python反序列化示例

下面的示例展示了如何使用Python从二进制数据反序列化回Person对象。

from person_pb2 import Person
import json

def main():
    serialized = b'\n\x0bJohn Doe\x12\x0bjohn.doe@example.com'
    person = Person()
    person.ParseFromString(serialized)

    print(f"Deserialized: {person.id} {person.name} {person.email}")

if __name__ == "__main__":
    main()

Go序列化示例

下面的示例展示了如何使用Go将一个Person对象序列化为二进制数据。

package main

import (
    "fmt"
    "google.golang.org/protobuf/proto"
    "google.golang.org/protobuf/types/descriptor"
)

type Person struct {
    Id   int32
    Name string
    Email string
}

func main() {
    person := &Person{
        Id:   1234,
        Name: "John Doe",
        Email: "john.doe@example.com",
    }

    // 消息对象序列化为二进制数据
    serialized, _ := proto.Marshal(person)
    fmt.Println("Serialized:", serialized)
}

Go反序列化示例

下面的示例展示了如何使用Go从二进制数据反序列化回Person对象。

package main

import (
    "fmt"
    "google.golang.org/protobuf/proto"
    "google.golang.org/protobuf/types/descriptor"
)

type Person struct {
    Id   int32
    Name string
    Email string
}

func main() {
    serialized := []byte{0x08, 0x04, 0x10, 0x0b, 0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x44, 0x6f, 0x65, 0x12, 0x1a, 0x6a, 0x6f, 0x68, 0x6e, 0x2e, 0x64, 0x6f, 0x65, 0x40, 0x65, 0x61, 0x73, 0x74, 0x72, 0x6f, 0x75, 0x73, 0x70, 0x65, 0x6c, 0x2e, 0x63, 0x6f, 0x6d}
    person := &Person{}
    proto.Unmarshal(serialized, person)

    fmt.Println("Deserialized:", person.Name, person.Id, person.Email)
}
常见问题解答

常见错误及解决方法

  1. 编译错误:找不到消息定义。

    确保.proto文件的路径和编译命令中的路径一致。例如,如果.proto文件位于src/main/proto目录下,编译命令应该是:

    protoc --java_out=src/main/java src/main/proto/Person.proto
  2. 序列化和反序列化不匹配。

    检查消息的字段编号是否一致。在.proto文件中,每个字段在定义时都有一个唯一的编号,确保序列化和反序列化时使用的字段编号一致。

常见问题汇总

  1. 如何更新消息格式?

    如果需要更新消息格式,可以在.proto文件中添加新的字段,然后重新编译.proto文件。旧版本的二进制数据仍然可以被反序列化,但是新添加的字段在旧数据中将不会包含任何值。

  2. 如何处理版本兼容性问题?

    .proto文件中使用option optimize_for = LITE_RUNTIME;来优化轻量级运行时,或者使用版本号来管理不同的消息格式版本。例如,可以在.proto文件中定义版本号:

    option java_package = "com.example.v1";
    option java_outer_classname = "PersonProto";
    option java_multiple_files = true;

    这样可以在不同的版本中定义不同的.proto文件,例如Person_v1.protoPerson_v2.proto

进一步学习资源

Protobuf官方文档推荐

社区和论坛推荐

通过以上内容,你已经掌握了Protobuf序列化和反序列化的基础概念、安装与配置方法、基础语法以及示例代码。希望这些信息对你有所帮助。如果你想更深入地了解Protobuf,可以参考官方文档和社区资源。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消