联系方式
Java服务器开发群:66728073
游戏开发者高级群:398808948
Unity3d游戏开发:286114103
MessageBox 一款新的序列化工具
2017-06-25 22:21浏览数:726 

对象序列化工具,集合了json和protobuf的优点,既灵活又有效率。源码地址:http://git.oschina.net/yunaibao/MessageBox



全新的序列化工具,参考json和protobuf实现的一个即具有灵活性也具有效率的序列化工具(这是一种折中策略,将json和protobuf两者优点有点平均)

经过测试,性能已经超过protostuff-runtime这个序列化工具

支持基本数据类型和基本数据类型的包装类型 以及  Enum 、Collection 、Map 、Date 、Calendar 、自定义的消息结构体(必须实现IMessage接口)

序列化的消息必须实现IMessage接口,消息体内支持Object类型的字段,字段值实际类型 可以是 基本类型或者Eum Collection Map等当前序列化工具所支持的类型 ,不支持Object类型

序列化: byte[] data = MessageBox.toBytes(object); 反序列化: MessageBox.parse(clazz, data);

具体使用,请参考项目中的test源码

目前只支持java

需要大家先测试java版,提交意见,完善java版之后,会出C#版

测试时,应当先执行序列化一遍,因为第一次序列化,会通过反射创建对象Schema,所以第一次序列化比较耗时,如果测试会有误差

不求赞,只求测试给意见和建议

-- 这里还是写一下例子吧

Message定义 :

Message必须实现IMessage接口,所有要序列化的字段必须标有@Member(tag) 注解,tag是字段的唯一int类型的标识

import java.util.Collection;
import java.util.Map;

import com.ynb.msgbox.IMessage;
import com.ynb.msgbox.annotation.Member;

@SuppressWarnings("rawtypes")
public class TestMessage implements IMessage{
    @Member(1)
    public TestEnum a;
    @Member(2)
    public int b;
    @Member(3)
    public int c;
    @Member(4)
    private Map map;
    @Member(5)
    private Map<String,Object> map2;
    @Member(6)
    private Collection<Object> collect;
    @Member(7)
    private Collection<Long> collect2;

    public Map getMap() {
        return map;
    }
    public void setMap(Map map) {
        this.map = map;
    }
    public Map<String, Object> getMap2() {
        return map2;
    }
    public void setMap2(Map<String, Object> map2) {
        this.map2 = map2;
    }
    public Collection<?> getCollect() {
        return collect;
    }
    public Collection<Long> getCollect2() {
        return collect2;
    }
    public void setCollect2(Collection<Long> collect2) {
        this.collect2 = collect2;
    }
    public void setCollect(Collection<Object> collect) {
        this.collect = collect;
    }
}

序列化和反序列化的使用

TestMessage msg = new TestMessage();
msg.a = TestEnum.c;
msg.b = 2;
msg.c = 1010;
msg.setCollect(coll1);
msg.setCollect2(coll2);
msg.setMap(map);
msg.setMap2(map2);

// 测试序列化消息体
byte[] bytes = MessageBox.toBytes(msg);

// 测试反序列化消息体
MessageBox.parse(TestMessage.class,bytes);

数据容器向消息体转换

MessageBox msgBox = new MessageBox();
msgBox.setAttr(1, TestEnum.a);
msgBox.setAttr(2, 123123);
msgBox.setAttr(3, 3);
//将msgBox数据容器转换为TestMessage对象
msgBox.toMessage(TestMessage.class);

不明确类型的反序列化

如果序列化一个对象得到数据后,对方并不知道这个对象的具体类型,那么此时使用 MessageBox.parse(bytes); 可以得到一个默认类型的数据

源类型反序列化后的类型
bytebyte
shortshort
intint
longlong
floatfloat
doubledouble
LinkedList,HashSet...ArrayList
ConcurrentHashMap,LinkedHahsMap...HashMap
MessageBox,other MessageMessageBox
Enumint
多维数组最后维度数组的组件类型为Object的多维数组(如:int[][][]   转为  Object[][][])
TestMessage msg = new TestMessage();
msg.a = TestEnum.c;
msg.b = 2;
msg.c = 1010;
msg.setCollect(coll1);
msg.setCollect2(coll2);
msg.setMap(map);
msg.setMap2(map2);

// 序列化消息体
byte[] bytes = MessageBox.toBytes(msg);

// 反序列化
MessageBox.parse(bytes);

字段类型不明确的序列化和反序列化

当Message中存在Object类型,该类型是不明确的,因此在序列化时,根据这个字段的值的实际类型o.getClass()来查找指定的Schema进行序列化操作
而明确类型的字段的Schema都在创建当前Message的Schema时已经查找并存起来,用以提高序列化效率
无论字段类型是否明确,都会将字段标注的@Member(tag) 和字段值的真实类型的基本类型id进行位运算写入到IO中
序列化后得到的数据,如果再反序列化时,因为字段类型不明确,会根据读到的tag解析出字段原有的基本类型id,再根据这个基本类型查找基本类型的Schema进行反序列化,此时就得到的是一个基本类型的值,参考上表

stirng序列化的优化

string 本身有getBytes()功能,但是原理是copy char[] 到charbuffer中,然后再创建bytebuffer,将这两个交给指定的编码器编码,
编码器编码时,解出每个char的指定编码格式的代码点,写入bytebuffer中,这个过程就已经存在了一个char[]数组的复制,和bytebuffer中初始化一定长度的byte[]数组
还有就是每次写入bytebuffer中,有可能也存在数组复制。当写入完毕,bytebuffer调用arry()方法,将bytebuffer中缓存的数据copy出来,再写入外部的IO中
这整个过程就存在了多次的数组复制,因此为了提高效率,此工具重写了UTF8的编码,直接嵌入到IO层,避免数组多次复制,因此提高了不少的性能