ProtoBuf 作为一种跨平台、语言无关、可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储。随着互联网的发展,系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代 Restful 之势,而 ProtoBuf 作为 gRPC 跨语言、高性能的法宝,我们技术人有必要深入理解 ProtoBuf 原理,为以后的技术更新和选型打下基础。
序列化框架
如何评价
我们评判一个编解码框架的优劣时,往往会考虑以下几个因素。
- 是否支持跨语言,支持的语言种类是否丰富
- 编码后的码流大小编解码的性能
- 类库是否小巧,API使用是否方便
- 使用者需要手工开发的工作量和难度
Java序列化的缺点
基于 Java 提供的对象输入输出流 ObjectInputStream 和 ObjectOutputStream,实现了 Java 对象实例的编解码。但在 RPC 场景下,很少直接使用 Java 序列化进行消息的编解码和传输,主要因为以下这两个方面:
- 无法跨语言
- 序列化后码流太大
常用的序列化方式
三种常用序列化方式优缺点,如下所示:
首先我们来看下为什么不使用 XML,尽管 XML 的可读性和可扩展性非常好,也非常适合描述数据结构,但是 XML 解析的时间开销和 XML 为了可读性而牺牲的空间开销都非常大,因此不适合做高性能的通信协议。Protobuf 使用二进制编码,在空间和性能上具有更大的优势。
Protobuf 另一个比较吸引人的地方就是它的数据描述文件和代码生成机制,利用数据描述文件对数据结构进行说明的优点如下:
- 文本化的数据结构描述语言,可以实现语言和平台无关,特别适合异构系统间的集成
- 通过标识字段的顺序,可以实现协议的前向兼容
- 自动代码生成,不需要手工编写同样数据结构的不同语言版本
- 方便后续的管理和维护。相比于代码,结构化的文档更容易管理和维护。
Google Protobuf
Protobuf 全称 Google Protocol Buffers,它将数据结构以.proto
文件进行描述,通过代码生成工具可以生成对应数据结构的 POJO 对象和 Protobuf 相关的方法和属性。
官方文档见 Protocol Buffers Introduction
API 文档见 Protocol Buffers API Documents
数据类型
基本数据类型
//采用proto3的语法
syntax = "proto3";
//结构体
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
下表为 protobuf 支持的基本数据类型
.proto Type | Notes | C++ Type | Java Type | Go Type |
---|---|---|---|---|
double | double | double | float64 | |
float | float | float | float32 | |
int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int32 |
int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int64 |
uint32 | Uses variable-length encoding. | uint32 | int[1] | uint32 |
uint64 | Uses variable-length encoding. | uint64 | long[1] | uint64 |
sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int32 |
sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int64 |
fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 228. | uint32 | int[1] | uint32 |
fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. | uint64 | long[1] | uint64 |
sfixed32 | Always four bytes. | int32 | int | int32 |
sfixed64 | Always eight bytes. | int64 | long | int64 |
bool | bool | boolean | bool | |
string | A string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232. | string | String | string |
bytes | May contain any arbitrary sequence of bytes no longer than 232. | string | ByteString | []byte |
枚举类型
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
//Enum's first constant must map to zero,
//so that we can use 0 as a numeric default value.
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
}
Corpus corpus = 2;
}
注:定义在其他 message 中的 Enumerator 可通过
_MessageType_._EnumType_
访问。
配置项:allow_alias
通过配置allow_alias = true
可以让不同的枚举项映射到同一个值。
message MyMessage1 {
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
message MyMessage2 {
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
}
字典类型
map<key_type, value_type> map_field = field_numbers;
key_type
支持任何整型和字符串类型(即,排除 floating point types、enum and bytes
).value_type
支持除了 map 类型的任何类型。
详细内容见 Maps
向后兼容
message MapFieldEntry { key_type key = 1; value_type value = 2; } repeated MapFieldEntry map_field = N;
导入类型
通过 import 导入 protobuf 自带的包或者自定义的包,并引用其内部定义的类型。
syntax = "proto3";
import "google/protobuf/wrappers.proto";
message Guide {
google.protobuf.StringValue str = 1;
}
数据编码
Base 128 Varints
Varints 即可变整型,根据提出的 msb 概念进行数据的切分。
Varints are a method of serializing integers using one or more bytes. Smaller numbers take a smaller number of bytes.
Each byte in a varint, except the last byte, has the most significant bit (msb) set – this indicates that there are further bytes to come. The lower 7 bits of each byte are used to store the two’s complement representation of the number in groups of 7 bits, least significant group first.
例一:计算 01
的值
01 -> 0000 0001
-> 000 0001 (delete msb)
-> (varint 1)
例二:计算 9E A7 05
的值
9E A7 05 -> 1001 1110 (original 9E) 1010 0111 (original A7) 0000 0101 (original 05)
-> 001 1110 (original 9E) 010 0111 (original A7) 000 0101 (original 05)
-> 101 (original 05) + 010 0111 (original A7) + 001 1110 (original 9E)
-> 101 010 0111 001 1110
-> (varint 86942)
简单的来说,msb 为 0 是代表整个可变整型组的最后一个数据,而 msb 为 1 是代表整个可变整型组的第一或中间的数据。
注:对于使用 msb 来分离数据的方式,计算过程其实有两种:
一种是对当前数左移与之前结果进行或操作
result |= data[i] << offset; offset += 7;
另一种是对当前结果左移与当前数进行或操作
result <<= offset; result |= data[i];
但是测试结果很神奇!居然是第二种方式计算效率高于第一种?难道 Protobuf 一顿编码猛如虎,实际操作 0-5 ?
Message Structure
message 中字段的编码结构为 (field_number << 3) | wire_type
varint
。
message Request {
//int32 对应 wire_type
//1 对应 field_number
//该字段的值对应 varint
int32 id = 1;
}
The available wire types are as follows:
Type | Meaning | Used For |
---|---|---|
0 | Varint | int32, int64, uint32, uint64, sint32, sint64, bool, enum |
1 | 64-bit | fixed64, sfixed64, double |
2 | Length-delimited | string, bytes, embedded messages, packed repeated fields |
3 | Start group | groups (deprecated) |
4 | End group | groups (deprecated) |
5 | 32-bit | fixed32, sfixed32, float |
More Value Types
Signed Integers
我们需要了解 the signed int types (sint32
and sint64
) and the “standard” int types (int32
and int64
) 的区别。将负数编码时,如果使用 the standard int types,作为结果的变长整型将固定为 10 bytes 的长度。而如果使用 the signed int types,作为结果的变长整型将采用更高效的 ZigZag 编码。
下表为 ZigZag 编码的示例及计算方式
Signed Original | Encoded As |
---|---|
0 | 0 |
-1 | 1 |
1 | 2 |
-2 | 3 |
2147483647 | 4294967294 |
-2147483648 | 4294967295 |
在sint32
中,ZigZag 编码计算方式为
(n << 1) ^ (n >> 31)
在sint64
中,ZigZag 编码计算方式为
(n << 1) ^ (n >> 63)
Strings
message Test2 {
optional string b = 2;
}
编码分析
12 07 [74 65 73 74 69 6e 67]
0x12
-> 0001 0010 (binary representation)
-> 00010 010 (regroup bits)
-> field_number = 2, wire_type = 2
0x07
-> 0000 0111
-> group_len = 7
其中,组中的数字为对应字符的 UTF-8 编码。
Embedded Messages
message Test1 {
optional int32 a = 1;
}
message Test3 {
optional Test1 c = 3;
}
Test3 编码分析
1a 03 08 96 01
0x1a
-> 00011 010 -> field_number = 3, wire_type = 2
0x03
-> group_len = 3
[08 96 01]
-> 08 -> 0000 1000 -> field_number = 1, wire_type = 0
-> 96 01 -> 150
整体过程可以解释为,为 Test3 设置其成员变量 Test1,为 Test1 中 field_number 为 1 的字段设置值 150。
Optional Elements
在 proto3 语法中,Optional Elements 即不带 repeated 对于一个没有用 repeated 关键字标注的字段,其值只会出现 0 或 1 次。
However, parsers are expected to handle the case in which they do. For numeric types and strings, if the same field appears multiple times, the parser accepts the last value it sees. For embedded message fields, the parser merges multiple instances of the same field, as if with the method – that is, all singular scalar fields in the latter instance replace those in the former, singular embedded messages are merged, and repeated fields are concatenated. The effect of these rules is that parsing the concatenation of two encoded messages produces exactly the same result as if you had parsed the two messages separately and merged the resulting objects. That is, this:
Message::MergeFrom
Repeated Elements
message Test4 {
repeated int32 d = 4 [packed=true];
//与上相同
//repeated int32 d = 4
}
在 proto3 语法当中,只有使用到 varint, 32-bit, or 64-bit wire type 的类型可以声明 packed 参数,且默认为 true。其作用主要体现在,当 repeated 字段进行编码时,如果 packed 参数为 true 且不包含任何元素时,不对该字段进行编码。
22 // key (field number 4, wire type 2)
06 // payload size (6 bytes)
03 // first element (varint 3)
8E 02 // second element (varint 270)
9E A7 05 // third element (varint 86942)
Importing Definitions
Note that this feature is not available in Java.
In the above example, the Result
message type is defined in the same file as SearchResponse
– what if the message type you want to use as a field type is already defined in another .proto
file?
You can use definitions from other .proto
files by importing them. To import another .proto
‘s definitions, you add an import statement to the top of your file:
import "myproject/other_protos.proto";
By default you can only use definitions from directly imported .proto
files. However, sometimes you may need to move a .proto
file to a new location. Instead of moving the .proto
file directly and updating all the call sites in a single change, now you can put a dummy .proto
file in the old location to forward all the imports to the new location using the import public
notion. import public
dependencies can be transitively relied upon by anyone importing the proto containing the import public
statement. For example:
// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto
The protocol compiler searches for imported files in a set of directories specified on the protocol compiler command line using the -I
/--proto_path
flag. If no flag was given, it looks in the directory in which the compiler was invoked. In general you should set the --proto_path
flag to the root of your project and use fully qualified names for all imports.
Using proto2 Message Types
It’s possible to import proto2 message types and use them in your proto3 messages, and vice versa. However, proto2 enums cannot be used directly in proto3 syntax (it’s okay if an imported proto2 message uses them).
Nested Types
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
可以通过 _Parent_._Type_
的形式来重用 message 结构体
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
嵌套的规则就是保证不会有同级的重名 message 结构体即可
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
int64 ival = 1;
bool booly = 2;
}
}
message MiddleBB { // Level 1
message Inner { // Level 2
int32 ival = 1;
bool booly = 2;
}
}
}
更新Message须知
别改变已有的 field numbers
新增字段不会对旧版本 message 有影响。
最初,proto3 消息在解析过程中总是丢弃未知字段,但在版本 3.5 中,我们重新引入了未知字段的保存,以匹配原始 2 行为。在版本 3.5 及以后,未知字段在分析过程中保留,并包含在序列化输出中。
字段可以被删除,但在后续更新的过程中不能重用其 field_numbers。
可以使用自定义前缀标识其无效性,或者使用 reserved 关键字占位。
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; }
int32
,uint32
,int64
,uint64
, andbool
是相互兼容的。sint32
andsint64
相互兼容,但不与其他 int 类型兼容。string
andbytes
在字节长度及编码都为 UTF-8 时相互兼容。fixed32
兼容sfixed32
并且fixed64
兼容sfixed64
.Embedded messages 与一个包含 message 编码版本号的
bytes
兼容。enum
is compatible withint32
,uint32
,int64
, anduint64
in terms of wire format. 兼容是在存储格式上兼容,但反序列化时的展现形式是 language-dependent 的。Changing a single value into a member of a new
oneof
is safe and binary compatible. Moving multiple fields into a newoneof
may be safe if you are sure that no code sets more than one at a time. Moving any fields into an existingoneof
is not safe.
Any
message 类型 Any 可以在没有.proto
定义的情况下使用其内容
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
Oneof
如果有一条包含许多字段的消息,并且最多同时设置一个字段,您可以使用其中 oneof 来强制执行此行为并节省内存。
Oneof 字段类似于常规字段,除了 Oneof 共享内存的所有字段之外,最多可以同时设置一个字段。设置 Oneof 的任何成员都会自动清除所有其他成员。您可以使用 case() 或 WhichOneof() 方法检查 Oneof 中的哪个值被设置(如果有的话),具体取决于选择的语言。
使用Oneof
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
向后兼容性问题
添加或删除 oneof 字段时要小心。如果检查 oneof 的值会返回 None / NOT _ SET,这可能意味着其中一个没有被设置,或者它已经被设置为 oneof 的不同版本中的字段。没有办法区分这两者,因为没有办法知道线上的未知区域是否是oneof的成员。
Tag Reuse Issues
- Move fields into or out of a oneof: You may lose some of your information (some fields will be cleared) after the message is serialized and parsed. However, you can safely move a single field into a new oneof and may be able to move multiple fields if it is known that only one is ever set.
- Delete a oneof field and add it back: This may clear your currently set oneof field after the message is serialized and parsed.
- Split or merge oneof: This has similar issues to moving regular fields.
Packages
通过设置 package 选项,可以防止协议消息类型之间的命名冲突。
package foo.bar;
message Open { ... }
在定义时,可以在定义消息类型的字段上使用包名:
message Foo {
...
foo.bar.Open open = 1;
...
}
The way a package specifier affects the generated code depends on your chosen language:
- In C++ the generated classes are wrapped inside a C++ namespace. For example,
Open
would be in the namespacefoo::bar
. - In Java, the package is used as the Java package, unless you explicitly provide an
option java_package
in your.proto
file. - In Go, the package is used as the Go package name, unless you explicitly provide an
option go_package
in your.proto
file.
Defining Services
如果要将消息类型用于 RPC(远程过程调用)系统,可以在.proto
文件中定义 RPC 服务接口和协议缓冲编译器将使用您选择的语言生成服务接口代码和存根。因此,如果您想用一种接受您的搜索请求并返回搜索响应的方法来定义 RPC 服务,您可以在 .proto 文件中定义它。 如下:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
与协议缓冲区一起使用的最直接的 RPC 系统是 gRPC : Google 开发的语言和平台无关的开源 RPC 系统。gRPC 与协议缓冲区配合得特别好,并允许您直接从使用特殊协议缓冲编译插件的 .proto 文件中生成相关的 RPC 代码。
如果你不想使用gRPC,也可以在你自己的RPC实现中使用协议缓冲区。你可以在 Proto2 Language Guide 中找到更多关于这一点的信息。
Options
Options 的全部内容被定义在 google/protobuf/descriptor.proto
。下表是 Options 级别:
- file-level meaning they should be written at the top-level scope
- message-level meaning they should be written inside message definitions
- field-level meaning they should be written inside field definitions
Options can also be written on enum types, enum values, oneof fields, service types, and service methods; however, no useful options currently exist for any of these.
详细内容见 Options
JSON Mapping
Proto3 支持转化为 JSON 格式映射。
proto3 | JSON | JSON example | Notes |
---|---|---|---|
message | object | {"fooBar": v, "g": null, …} |
Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type. |
enum | string | "FOO_BAR" |
The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values. |
map |
object | {"k": v, …} |
All keys are converted to strings. |
repeated V | array | [v, …] |
null is accepted as the empty list [] . |
bool | true, false | true, false |
|
string | string | "Hello World!" |
|
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" |
JSON value will be the data encoded as a string using standard base64 encoding with paddings. Either standard or URL-safe base64 encoding with/without paddings are accepted. |
int32, fixed32, uint32 | number | 1, -10, 0 |
JSON value will be a decimal number. Either numbers or strings are accepted. |
int64, fixed64, uint64 | string | "1", "-10" |
JSON value will be a decimal string. Either numbers or strings are accepted. |
float, double | number | 1.1, -10.0, 0, "NaN", "Infinity" |
JSON value will be a number or one of the special string values “NaN”, “Infinity”, and “-Infinity”. Either numbers or strings are accepted. Exponent notation is also accepted. -0 is considered equivalent to 0. |
Any | object |
{"@type": "url", "f": v, … } |
If the Any contains a value that has a special JSON mapping, it will be converted as follows: {"@type": xxx, "value": yyy} . Otherwise, the value will be converted into a JSON object, and the "@type" field will be inserted to indicate the actual data type. |
Timestamp | string | "1972-01-01T10:00:20.021Z" |
Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than “Z” are also accepted. |
Duration | string | "1.000340012s", "1s" |
Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix “s”. Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix “s” is required. |
Struct | object |
{ … } |
Any JSON object. See struct.proto . |
Wrapper types | various types | 2, "2", "foo", true, "true", null, 0, … |
Wrappers use the same representation in JSON as the wrapped primitive type, except that null is allowed and preserved during data conversion and transfer. |
FieldMask | string | "f.fooBar,h" |
See field_mask.proto . |
ListValue | array | [foo, bar, …] |
|
Value | value | Any JSON value. Check google.protobuf.Value for details. | |
NullValue | null | JSON null | |
Empty | object | {} |
An empty JSON object |
JSON options
A proto3 JSON implementation may provide the following options:
- Emit fields with default values: Fields with default values are omitted by default in proto3 JSON output. An implementation may provide an option to override this behavior and output fields with their default values.
- Ignore unknown fields: Proto3 JSON parser should reject unknown fields by default but may provide an option to ignore unknown fields in parsing.
- Use proto field name instead of lowerCamelCase name: By default proto3 JSON printer should convert the field name to lowerCamelCase and use that as the JSON name. An implementation may provide an option to use proto field name as the JSON name instead. Proto3 JSON parsers are required to accept both the converted lowerCamelCase name and the proto field name.
- Emit enum values as integers instead of strings: The name of an enum value is used by default in JSON output. An option may be provided to use the numeric value of the enum value instead.
生成Skeleton
详细内容见 Generating Your Classes
Java语言Maven方式构建
引入 protobuf 和 grpc 的包
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.0.0-rc-2</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
<version>1.37.0</version>
</dependency>
引入 protobuf-maven-plugin 插件
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.29.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
执行以下两个命令,默认读取src/main/proto
路径下的 proto 文件:
protobuf:compile
生成序列化类protobuf:compile-custom
生成 Grpc传输类
注意:IDEA在执行这两个命令之后可能不能正确显示生成的文件,记得
Reload from Disk
。如果 target 中已有生成的文件但不能正常检索,在 Project Structure 中为target\generated-sources\protobuf
目录下的grpc-java
、java
文件夹 mark 为 Source 即可。
各序列化框架性能测试
SIMPLE/GENERIC:
Serializes any POJO tree without class specific optimization. Serialized classes are known in advance. No cycle detection/shared object detection is done.
create ser deser total size +dfl
java-built-in 63 5838 30208 36046 889 514
hessian 63 3881 6176 10057 501 313
kryo 63 655 838 1493 212 132
fast-serialization 63 704 864 1568 252 166
jboss-serialization 63 6466 6643 13110 932 582
jboss-marshalling-river 63 4656 23892 28548 694 400
protostuff 82 495 732 1227 239 150
msgpack-databind 62 830 1370 2200 233 146
json/jackson/databind 62 1895 2600 4496 485 261
json/jackson/db-afterburner 63 1513 1988 3501 485 261
json/protostuff-runtime 63 1532 2138 3670 469 243
json/google-gson/databind 63 5633 4844 10477 486 259
json/svenson-databind 63 5270 10358 15628 495 272
json/flexjson/databind 63 19445 25394 44838 503 273
json/fastjson/databind 63 1316 1149 2465 486 262
smile/jackson/databind 63 1768 1891 3659 338 241
smile/jackson/db-afterburner 64 1448 1492 2940 352 252
bson/jackson/databind 64 5376 6812 12188 506 286
xml/xstream+c 64 6476 13505 19981 487 244
xml/jackson/databind-aalto 63 3001 5516 8517 683 286
DEFAULT:
Serializes arbitrary object graphs, cycle detection enabled. Nothing is known in advance about the classes to serialize. Only serializers supporting full object graph serialization are included.
create ser deser total size +dfl
java-built-in-serializer 64 5723 29259 34982 889 514
hessian 64 3611 6169 9780 501 313
kryo-serializer 64 1711 1499 3210 311 198
fast-serialization-shared 64 1621 1592 3212 341 212
jboss-serialization 64 6442 6339 12781 932 582
SIMPLE/SPECIFC:
Serializes only specific classes using code generation or other special knowledge about the class.
create ser deser total size +dfl
kryo-opt 64 658 864 1522 209 129
wobly 43 886 536 1422 251 151
wobly-compact 43 903 569 1471 225 139
protobuf 130 1225 701 1926 239 149
protostuff 82 488 678 1166 239 150
protobuf/protostuff 83 598 692 1290 239 149
thrift 126 1796 795 2591 349 197
thrift-compact 126 1555 963 2518 240 148
avro 89 1616 1415 3031 221 133
json/json-lib-databind 63 26330 103150 129479 485 263
json/jsonij-jpath 63 38015 12325 50339 478 259
MANUAL:
Serializes only specific classes using hand written serialization code.
create ser deser total size +dfl
java-manual 63 847 632 1480 255 147
kryo-manual 63 555 616 1171 211 131
protostuff-manual 63 465 711 1176 239 150
avro-generic 379 1822 1125 2947 221 133
json/jackson/manual 63 1097 1539 2636 468 253
json/protostuff-manual 63 1345 1816 3161 449 233
json/google-gson/manual 63 3696 3756 7452 468 253
json/json.simple/manual 63 6184 8059 14243 495 269
json/json-smart/manual/tree 63 5314 4088 9402 495 269
json/org.json/manual/tree 63 6989 8413 15403 485 259
json/argo-manual/tree 63 66575 14578 81153 485 263
smile/jackson/manual 63 939 1092 2031 341 244
bson/mongodb 64 3422 7762 11184 495 278
xml/woodstox-manual 63 3159 4578 7737 653 304
xml/aalto-manual 63 2077 3093 5170 653 304
xml/xstream+c-woodstox 63 5638 10506 16144 525 273
xml/xstream+c-aalto 63 4893 8912 13805 525 273
xml/xstream+c-fastinfo 63 8451 7971 16422 345 264
xml/javolution 64 5544 8538 14082 504 263
xml/fastinfo-manual 64 6959 5420 12379 377 284