文章目录

在定义protobuf消息时,有时候需要用到extensions来对原有的消息类型进行扩展,有利于消息定义的重复使用。

1、下面写一个最简单的例子,定义一个message BaseData,并对其进行扩展:

Example.proto:

1
2
3
4
5
6
7
8
9
10
// 定义一个message BaseData,100~199之间的tag可供扩展
message BaseData {
required int32 code = 1;
extensions 100 to 199;
}

// 扩展BaseData,加上一个extend_data,tag为100
extend BaseData {
required string extend_data = 100;
}

运行protoc.exe —java_out=. Example.proto生成Example.java文件,将这个Java文件加入到项目中。

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws IOException {

Example.BaseData.Builder baseBuilder = Example.BaseData.newBuilder();
baseBuilder.setCode(123);
baseBuilder.setExtension(Example.extendData, "xxg");
Example.BaseData baseData = baseBuilder.build();

// -------------- 分割线:上面是发送方,将数据序列化后发送 ---------------

ByteArrayOutputStream output = new ByteArrayOutputStream();
baseData.writeTo(output);
byte[] byteArray = output.toByteArray();
ByteArrayInputStream input = new ByteArrayInputStream(byteArray);

// -------------- 分割线:下面是接收方,将数据接收后反序列化 ---------------

ExtensionRegistry registry = ExtensionRegistry.newInstance();
registry.add(Example.extendData); // 或者Example.registerAllExtensions(registry);
Example.BaseData receiveBaseData = Example.BaseData.parseFrom(input, registry);
System.out.println(receiveBaseData.getCode());
System.out.println(receiveBaseData.getExtension(Example.extendData));
}

输出:

123
xxg

这里序列化的时候需要调用setExtension方法来设置扩展的extendData。setExtension有两个参数,第一个定义扩展的是哪一个字段,Example.extendData即表示Example类中的extendData,第二个就是设置扩展字段的值。

反序列化时,一定要对extensions进行注册。定义一个ExtensionRegistry对象registry,用registry.add(Example.extendData)或者Example.registerAllExtensions(registry)注册extensions,再将registry传入parseFrom方法作为第二个参数。如果直接按下面的方式解析就拿不到extensions的数据:

1
2
3
4
5
// -------------- 分割线:下面是接收方,将数据接收后反序列化 ---------------

Example.BaseData receiveBaseData = Example.BaseData.parseFrom(input);
System.out.println(receiveBaseData.getCode());
System.out.println(receiveBaseData.getExtension(Example.extendData));

输出:

123

2、另外还有一种常用的扩展定义方式:

Example.proto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个message BaseData,100~199之间的tag可供扩展
message BaseData {
required int32 code = 1;
extensions 100 to 199;
}

// 定义一个message Data
message Data {
required string msg = 1;

// 扩展BaseData,加上一个Data类型的字段,tag为100
extend BaseData {
required Data extend_data = 100;
}
}

这种方式看起来有点奇怪,实际上可以理解成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个message BaseData,100~199之间的tag可供扩展
message BaseData {
required int32 code = 1;
extensions 100 to 199;
}

// 定义一个message Data
message Data {
required string msg = 1;
}

// 扩展BaseData,加上一个Data类型的字段,tag为100
extend BaseData {
required Data extend_data = 100;
}

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static void main(String[] args) throws IOException {

// 先构造一个message Data类型对象
Example.Data.Builder dataBuilder = Example.Data.newBuilder();
dataBuilder.setMsg("xxg");
Example.Data data = dataBuilder.build();

// 再构造message BaseData对象,将Data对象通过setExtension设置到BaseData中
Example.BaseData.Builder baseBuilder = Example.BaseData.newBuilder();
baseBuilder.setCode(123);
baseBuilder.setExtension(Example.Data.extendData, data);
Example.BaseData baseData = baseBuilder.build();

// -------------- 分割线:上面是发送方,将数据序列化后发送 ---------------

ByteArrayOutputStream output = new ByteArrayOutputStream();
baseData.writeTo(output);
byte[] byteArray = output.toByteArray();
ByteArrayInputStream input = new ByteArrayInputStream(byteArray);

// -------------- 分割线:下面是接收方,将数据接收后反序列化 ---------------

ExtensionRegistry registry = ExtensionRegistry.newInstance();
registry.add(Example.Data.extendData); // 或者Example.registerAllExtensions(registry);
Example.BaseData receiveBaseData = Example.BaseData.parseFrom(input, registry);
System.out.println(receiveBaseData.getCode());
System.out.println(receiveBaseData.getExtension(Example.Data.extendData).getMsg());
}

输出:

123
xxg

注意上面的Java代码用的是第一种proto消息定义方式,如果用的是下面第二种消息定义的方式,setExtension的参数Example.Data.extendData就应该改成Example.extendData,注意嵌套结构。

文章目录