Protostuff
The goal of protostuff is to generate a schema whether at compile-time or runtime and use that for reading/writing to various formats via the provided IO libs.
Schema
A class that encapsulates:
- the serialization logic of an object
- the deserialization logic of an object
- the validation of an object’s required fields
- the mapping of an object’s field names to field numbers
- the instantiation of the object.
For existing objects, use protostuff-runtime which uses reflection.
示例
User类是个简单的pojo类:
package demo.domain;
import lombok.Data;
import java.util.List;
@Data
public class User {
private String firstName;
private String lastName;
private String email;
private List<User> friends;
}
定义User的序列化逻辑:UserSchema
package demo.serializing;
import demo.domain.User;
import io.protostuff.Input;
import io.protostuff.Output;
import io.protostuff.Schema;
import io.protostuff.UninitializedMessageException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
public class UserSchema implements Schema<User> {
private static final HashMap<String, Integer> fieldMap = new HashMap<>();
static {
fieldMap.put("email", 1);
fieldMap.put("firstName", 2);
fieldMap.put("lastName", 3);
fieldMap.put("friends", 4);
}
@Override
public String getFieldName(int number) {
switch (number) {
case 1:
return "email";
case 2:
return "firstName";
case 3:
return "lastName";
case 4:
return "friends";
default:
return null;
}
}
@Override
public int getFieldNumber(String name) {
Integer number = fieldMap.get(name);
return number == null ? 0 : number;
}
@Override
public boolean isInitialized(User message) {
return message.getEmail() != null;
}
@Override
public User newMessage() {
return new User();
}
@Override
public String messageName() {
return User.class.getSimpleName();
}
@Override
public String messageFullName() {
return User.class.getName();
}
@Override
public Class<? super User> typeClass() {
return User.class;
}
@Override
public void mergeFrom(Input input, User message) throws IOException {
while (true) {
int number = input.readFieldNumber(this);
switch (number) {
case 0:
return;
case 1:
message.setEmail(input.readString());
break;
case 2:
message.setFirstName(input.readString());
break;
case 3:
message.setLastName(input.readString());
break;
case 4:
if (message.getFriends() == null)
message.setFriends(new ArrayList<>());
message.getFriends().add(input.mergeObject(null, this));
break;
default:
input.handleUnknownField(number, this);
}
}
}
@Override
public void writeTo(Output output, User user) throws IOException {
if (user.getEmail() == null)
throw new UninitializedMessageException(user, this);
output.writeString(1, user.getEmail(), false);
if (user.getFirstName() != null)
output.writeString(2, user.getFirstName(), false);
if (user.getLastName() != null)
output.writeString(3, user.getLastName(), false);
if (user.getFriends() != null) {
for (User friend : user.getFriends()) {
if (friend != null)
output.writeObject(4, friend, this, true);
}
}
}
}
序列化和反序列化示例:
package demo;
import demo.domain.User;
import demo.serializing.UserSchema;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import lombok.extern.java.Log;
import java.util.ArrayList;
import java.util.List;
@Log
public class App {
public static void main(String[] args) {
User user1 = new User();
user1.setEmail("1178449100@qq.com");
user1.setFirstName("wenwen");
user1.setLastName("zha");
User user2 = new User();
user2.setEmail("gumengqin@qq.com");
List<User> users = new ArrayList<>();
users.add(user2);
user1.setFriends(users);
Schema<User> schema = new UserSchema();
byte[] data;
data = ProtostuffIOUtil.toByteArray(user1, schema, LinkedBuffer.allocate());
log.info("序列化完成:" + data.length);
User newUser = new User();
ProtostuffIOUtil.mergeFrom(data, newUser, schema);
log.info("反序列化完成:" + newUser);
}
}
RuntimeSchema
使用RuntimeSchema可以不用自定义Schema,省了不少工作。
package demo.serializing;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ProtostuffUtils {
//避免每次序列化都重新申请Buffer空间
private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
//缓存Schema
private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<Class<?>, Schema<?>>();
//序列化方法,把指定对象序列化成字节数组
@SuppressWarnings("unchecked")
public static <T> byte[] serialize(T obj) {
Class<T> clazz = (Class<T>) obj.getClass();
Schema<T> schema = getSchema(clazz);
byte[] data;
try {
data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} finally {
buffer.clear();
}
return data;
}
//反序列化方法,将字节数组反序列化成指定Class类型
public static <T> T deserialize(byte[] data, Class<T> clazz) {
Schema<T> schema = getSchema(clazz);
T obj = schema.newMessage();
ProtostuffIOUtil.mergeFrom(data, obj, schema);
return obj;
}
@SuppressWarnings("unchecked")
private static <T> Schema<T> getSchema(Class<T> clazz) {
Schema<T> schema = (Schema<T>) schemaCache.get(clazz);
if (schema == null) {
schema = RuntimeSchema.getSchema(clazz);
if (schema != null) {
schemaCache.put(clazz, schema);
}
}
return schema;
}
}
重新测试:
package demo;
import demo.domain.User;
import demo.serializing.ProtostuffUtils;
import lombok.extern.java.Log;
import java.util.ArrayList;
import java.util.List;
@Log
public class App {
public static void main(String[] args) {
User user1 = new User();
user1.setEmail("1178449100@qq.com");
user1.setFirstName("wenwen");
user1.setLastName("zha");
User user2 = new User();
user2.setEmail("gumengqin@qq.com");
List<User> users = new ArrayList<>();
users.add(user2);
user1.setFriends(users);
byte[] data = ProtostuffUtils.serialize(user1);
log.info("序列化完成:" + data.length);
User newUser=ProtostuffUtils.deserialize(data,User.class);
log.info("反序列化完成:" + newUser);
}
}