在使用Protostuff进行序列化的时候,不幸地遇到了一个问题,就是Timestamp作为字段的时候,转换出现问题,通过Protostuff转换后的结果都是1970-01-01 08:00:00,这就造成了Timestamp不能够序列化。于是Google了一番,得知可以用Delegate来解决这个问题。
ProtobufferCodec类
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
public class ProtobufferCodec implements Codec {
private static Map, Schema>> cachedSchema = new ConcurrentHashMap<>();
public ProtobufferCodec() {
}
@Override
public short getId() {
return Codecs.PROTOBUFFER_CODEC;
}
@SuppressWarnings("unchecked")
private static Schema getSchema(Class cls) {
Schema schema = (Schema) cachedSchema.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
if (schema != null) {
cachedSchema.put(cls, schema);
}
}
return schema;
}
@Override
public byte[] encode(T obj) {
if (obj == null) {
return null;
}
Class cls = (Class) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema schema = getSchema(cls);
byte[] bytes = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
return bytes;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
@Override
public T decode(byte[] bytes, Class clazz) {
if (bytes == null || bytes.length == 0) {
return null;
}
try {
Constructor constructor = clazz.getConstructor();
constructor.setAccessible(true);
T message = constructor.newInstance();
Schema schema = getSchema(clazz);
ProtostuffIOUtil.mergeFrom(bytes, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
Codec接口
/**
* 编解码器
* @author jiujie
* @version $Id: Codec.java, v 0.1 2016年3月31日 上午11:39:14 jiujie Exp $
*/
public interface Codec {
/**
* 编解码器ID,用于标识编解码器
* @author jiujie
* 2016年3月31日 上午11:38:39
* @return
*/
public short getId();
/**
* 把对象数据结构编码成一个DataBuffer
* @param
*/
public byte[] encode(T obj);
/**
* 把DataBuffer解包构造一个对象
* @param
*/
public T decode(byte[] bytes, Class clazz);
import java.sql.Timestamp;
import java.util.concurrent.ConcurrentHashMap;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.DefaultIdStrategy;
import io.protostuff.runtime.Delegate;
import io.protostuff.runtime.RuntimeEnv;
import io.protostuff.runtime.RuntimeSchema;
/**
* ProtoBuffer编解码
* @author jiujie
* @version $Id: ProtobufferCodec.java, v 0.1 2016年7月20日 下午1:52:41 jiujie Exp $
*/
public class ProtobufferCodec implements Codec {
/** 时间戳转换Delegate,解决时间戳转换后错误问题 @author jiujie 2016年7月20日 下午1:52:25 */
private final static Delegate TIMESTAMP_DELEGATE = new TimestampDelegate();
private final static DefaultIdStrategy idStrategy = ((DefaultIdStrategy) RuntimeEnv.ID_STRATEGY);
private final static ConcurrentHashMap, Schema>> cachedSchema = new ConcurrentHashMap<>();
static {
idStrategy.registerDelegate(TIMESTAMP_DELEGATE);
}
public ProtobufferCodec() {
}
@Override
public short getId() {
return Codecs.PROTOBUFFER_CODEC;
}
@SuppressWarnings("unchecked")
public static Schema getSchema(Class clazz) {
Schema schema = (Schema) cachedSchema.get(clazz);
if (schema == null) {
schema = RuntimeSchema.createFrom(clazz, idStrategy);
cachedSchema.put(clazz, schema);
}
return schema;
}
@Override
public byte[] encode(T obj) {
if (obj == null) {
return null;
}
@SuppressWarnings("unchecked")
Class cls = (Class) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema schema = getSchema(cls);
byte[] bytes = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
return bytes;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
@Override
public T decode(byte[] bytes, Class clazz) {
if (bytes == null || bytes.length == 0) {
return null;
}
try {
Schema schema = getSchema(clazz);
//改为由Schema来实例化解码对象,没有构造函数也没有问题
T message = schema.newMessage();
ProtostuffIOUtil.mergeFrom(bytes, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
TimestampDelegate类
import java.io.IOException;
import java.sql.Timestamp;
import io.protostuff.Input;
import io.protostuff.Output;
import io.protostuff.Pipe;
import io.protostuff.WireFormat.FieldType;
import io.protostuff.runtime.Delegate;
/**
* protostuff timestamp Delegate
* @author jiujie
* @version $Id: TimestampDelegate.java, v 0.1 2016年7月20日 下午2:08:11 jiujie Exp $
*/
public class TimestampDelegate implements Delegate {
public FieldType getFieldType() {
return FieldType.FIXED64;
}
public Class> typeClass() {
return Timestamp.class;
}
public Timestamp readFrom(Input input) throws IOException {
return new Timestamp(input.readFixed64());
}
public void writeTo(Output output, int number, Timestamp value,
boolean repeated) throws IOException {
output.writeFixed64(number, value.getTime(), repeated);
}
public void transfer(Pipe pipe, Input input, Output output, int number,
boolean repeated) throws IOException {
output.writeFixed64(number, input.readFixed64(), repeated);
}
}
使用方法:
实现Delegage接口,并在IdStrategy策略类中注册该Delegate。
使用场景:
当需要序列化的类的字段中有transient声明序列化时会过滤字段,导致还原时丢失信息的场景,或者一些需要高度自定义数据格式的场景下,可以使用Delegate来序列化与反序列化。
注意事项:
这个对象必须是另一个对象的字段时,这个Delegate才会生效,如果直接用Timestamp来转换,则还是不生效,这个问题与源码的实现有关,源码是检测对象的字段来调用Delegate的,如果本身直接过来序列化的时候,则不会触发Delegate。