zoukankan      html  css  js  c++  java
  • Protostuff自定义序列化(Delegate)解析

    背景

    在使用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<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();
    
        public ProtobufferCodec() {
    
        }
    
        @Override
        public short getId() {
            return Codecs.PROTOBUFFER_CODEC;
        }
    
        @SuppressWarnings("unchecked")
        private static <T> Schema<T> getSchema(Class<T> cls) {
            Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
            if (schema == null) {
                schema = RuntimeSchema.createFrom(cls);
                if (schema != null) {
                    cachedSchema.put(cls, schema);
                }
            }
            return schema;
        }
    
        @Override
        public <T> byte[] encode(T obj) {
            if (obj == null) {
                return null;
            }
            Class<T> cls = (Class<T>) obj.getClass();
            LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
            try {
                Schema<T> 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> T decode(byte[] bytes, Class<T> clazz) {
            if (bytes == null || bytes.length == 0) {
                return null;
            }
            try {
                Constructor<T> constructor = clazz.getConstructor();
                constructor.setAccessible(true);
                T message = constructor.newInstance();
                Schema<T> 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 <T>
         */
        public <T> byte[] encode(T obj);
    
        /**
         * 把DataBuffer解包构造一个对象
         * @param <T>
         */
        public <T> T decode(byte[] bytes, Class<T> 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>                    TIMESTAMP_DELEGATE = new TimestampDelegate();
    
        private final static DefaultIdStrategy                      idStrategy         = ((DefaultIdStrategy) RuntimeEnv.ID_STRATEGY);
    
        private final static ConcurrentHashMap<Class<?>, Schema<?>> cachedSchema       = new ConcurrentHashMap<>();
    
        static {
            idStrategy.registerDelegate(TIMESTAMP_DELEGATE);
        }
    
        public ProtobufferCodec() {
        }
    
        @Override
        public short getId() {
            return Codecs.PROTOBUFFER_CODEC;
        }
    
        @SuppressWarnings("unchecked")
        public static <T> Schema<T> getSchema(Class<T> clazz) {
            Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
            if (schema == null) {
                schema = RuntimeSchema.createFrom(clazz, idStrategy);
                cachedSchema.put(clazz, schema);
            }
            return schema;
        }
    
        @Override
        public <T> byte[] encode(T obj) {
            if (obj == null) {
                return null;
            }
            @SuppressWarnings("unchecked")
            Class<T> cls = (Class<T>) obj.getClass();
            LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
            try {
                Schema<T> 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> T decode(byte[] bytes, Class<T> clazz) {
            if (bytes == null || bytes.length == 0) {
                return null;
            }
            try {
                Schema<T> 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<Timestamp> {
        
        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。

  • 相关阅读:
    bzoj 2159 Crash 的文明世界
    bzoj 4241 历史研究
    数论大合集(柿子版)
    [IOI2005]mou
    CSP 2020 T3 函数调用
    线段树 --算法竞赛专题解析(24)
    树状数组 --算法竞赛专题解析(23)
    算法竞赛专题解析(22):数论--同余
    算法竞赛专题解析(21):数论--线性丢番图方程
    算法竞赛专题解析(20):数论--GCD和LCM
  • 原文地址:https://www.cnblogs.com/uwannerme/p/5692861.html
Copyright © 2011-2022 走看看