MessageData

高效率的数据交换格式-MessageData(二)应用

上文提到过MessageData转换的数据为字节数据,并且是依赖object的信息进行封装和解析的。在开发中我们也会经常将数据以对象的形式进行传递和使用,MessageData的核心就是将对象数据转换成字节数组,并能将字节数组还原成数据对象。

MessageData的原理

MessageData支持的数据类型有int(整型)、float(浮点型)、bool(布尔型)、byte(字节型)、short(短整型)、string(字符串),同时支持object数据以及List数据结构。

1.基本数据类型

class Data{ 
    public byte dataByte; 
    public bool dataBool; 
    public short dataShort;
    public int dataInt;
    public float dataFloat;
}

上面的Data类用来描述一组数据,通过MessageData将其转换为字节数组后的的结构如下图

MessageData转换基本数据类型

对应布尔型数据采用1个字节长度存储,短整型数据采用2个字节进行存储,整型和浮点型采用4个字节进行存储。MessageData转换一组上述数据后得到的字节数组的长度为12。

2.字符串类型

MessageData默认采用的是UTF8对字符串进行字节转换。由于字符串类型的数据长度不固定,为了方便转换,MessageData会先用2个字节来存储字符串的字节长度(MessageData最大支持2个字节长度的字符串数据,也就是65536个字节长度)。

MessageData对字符串数据进行转换

3.List列表类型

MessageData支持List类型数据,其列表项可以为基本数据类型、数据对象、List。

与字符串类似,对List类型数据的转换会先用2个字节来存储List的长度,而List的列表项会按照对应的类型进行转换存储。

MessageDada在转换对象与字节数据时依赖于对象的Class信息,在将对象转换成字节数据时,会对对象进行反射得到对象的Field信息,然后根据Field所描述的数据类型进行相应的转换。

MesaageData的使用

目前MessageData支持C#和JAVA两个平台,支持C#和JAVA之间互相解析。

常用API

名称 说明
构造方法
MessageData() 创建一个MessageData的实例
MessageData(byte[] data) 指定byte数组,创建一个MessageData实例
静态方法
MessageData CreateMessage(byte[] data) 指定byte数组,创建一个MessageData实例
方法
void AddInt(int value) 添加一个int类型数据
void AddShort(short value) 添加一个short类型数据
void AddBoolean(bool value) 添加一个bool类型数据
void AddFloat(float value) 添加一个float类型数据
void AddString(string value) 添加一个string类型数据
void AddBean(object bean) 添加一个bean类型对象数据(一组只包含基本数据类型的结构)
void AddObject(object obj) 添加一个对象数据(只会解析其公共参数或属性)
int GetInt() 从byte数组中获取一个int类型数据
short GetShort() 从byte数组中获取一个short类型数据
bool GetBoolean() 从byte数组中获取一个bool类型数据
float GetFloat() 从byte数组中获取一个float类型数据
string GetString() 从byte数组中获取一个string类型数据
void Packet2Bean(object bean) 将MessageData中的字节数据打包对象bean中
void Packet2Object(object obj) 通过反射将MessageData中的字节数据打包到obj对应的Filed中
object Packet2ObjectRef(object obj) 将MessageData中的字节数据打包到一个struct结构体中
byte[] ToDataBytes() 返回MessageData所构建的字节数据

1.对基本数据类型进行交换(C#演示)

        static void Main(string[] args)
        {
            MessageData md = new MessageData();
            md.AddInt(10);
            md.AddFloat(1.35f);
            md.AddBoolean(false);
            md.AddString("berry");
            byte[] transData = md.ToDataBytes();
            Console.WriteLine("转换后字节数组长度:"+transData.Length);
            

            MessageData md2 = MessageData.CreateMessage(transData);
            Console.WriteLine("GetInt:" + md2.GetInt());
            Console.WriteLine("GetFloat:" + md2.GetFloat());
            Console.WriteLine("GetBoolean:" + md2.GetBoolean());
            Console.WriteLine("GetString:" + md2.GetString());

            Console.ReadLine();
        }

创建MessageData实例后可以使用相应的Add方法,向其中添加对应的基本类型数据。调用ToDataBytes方法可以返回转换后的字节数组。

使用带参数的构造方法MessageData(byte[] data)(或静态方法CreateMessage(byte[] data)),创建MessageData实例后使用相应的Get方法可以得到对应的基本数据。

注:直接通过Add和Get方法操作基本类型数据时,Get方法的使用顺序要与Add方法一致。

2.对象类型的数据转换(C#演示)

使用对象来封装打包数据在在开发中十分常用。在接受到网络数据时,往往还要将其转换为数据对象来进行使用。

    public class User {
        public string name;
        public int age;
        public float score;
    }

User是一个用来描述用户信息的类

        static void Main(string[] args)
        {
            User user = new User("berry",1,6.6f);
            MessageData md = new MessageData();
            //将user中的数据添加到MessageData中
            md.AddObject(user);
            //获得MessageData转换到的字节数据
            byte[] data = md.ToDataBytes();
            Console.WriteLine("data的长度为:" + data.Length);
            //-------------------------------------------
            User emptyUser = new User();
            //通过data字节数据创建MessageData实例,并将data数据转换到emptyUser中
            MessageData.CreateMessage(data).Pakcet2Object(emptyUser);
            Console.WriteLine("name:" + emptyUser.name);
            Console.WriteLine("age:" + emptyUser.age);
            Console.WriteLine("score:" + emptyUser.score);
            Console.ReadLine();
        }

上面代码是将User对象数据转换成字节数据,再将字节数据转换到一个空的User对象中(序列化和反序列化对象)。使用AddObject方法来添加对象并使用ToDataBytes方法得到转换后的字节数据,使用转换后的字节数据创建MessageData实例并使用Packet2Object方法将字节数据打包到相应的空白对象中,如此来玩成一次数据交换。

对List数据进行交换操作

这里使用上面的User作为List的列表项

        static void Main(string[] args)
        {
            List<User> users = new List<User>();
            users.Add(new User("user1",10,5.5f));
            users.Add(new User("user2",20,4.4f));
            users.Add(new User("user3",16,8.6f));

            MessageData md = new MessageData();
            md.AddObject(users);
            byte[] data = md.ToDataBytes();
            Console.WriteLine("data的长度为:" + data.Length);
            //-------------------------------------------
            List<User> emptyUsers = new List<User>();
            MessageData.CreateMessage(data).Pakcet2Object(emptyUsers);
            for (int i = 0; i < emptyUsers.Count; i++) {
                Console.WriteLine("列表项."+i+" name:"+emptyUsers[i].name+
                    " age:"+emptyUsers[i].age+" score:"+emptyUsers[i].score);
            }
            Console.ReadLine();
        }

对List的转换依然是使用AddObject和Pakcet2Object这两个方法,其实对于引用类型的数据对象直接使用这两个方法就行了。

MessageData支持各种复杂的嵌套结构

    public class Room {
        public string roomName;
        public List<User> users = new List<User>();
        public Room() { }
        public Room(string roomName){
            this.roomName = roomName;
        }
    }


    public class User {
        public string name;
        public int age;
        public float score;
        public List<string> interests = new List<string>();
        public User() { }
        public User(string name,int age,float score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
    }

Room用来描述一个房间和房间下的所有User信息,这里的User包含了一个List表示爱好。接下来来转换这一结构数据。

        static void Main(string[] args)
        {
            Room room = new Room("树莓屋");
            User user1 = new User("user1", 10, 5.5f);
            user1.interests.Add("读书");
            user1.interests.Add("写字");
            User user2 = new User("user2", 20, 4.4f);
            user2.interests.Add("电影");
            User user3 = new User("user3", 16, 8.6f);
            user3.interests.Add("足球");
            user3.interests.Add("音乐");
            room.users.Add(user1);
            room.users.Add(user2);
            room.users.Add(user3);

            MessageData md = new MessageData();
            md.AddObject(room);
            byte[] data = md.ToDataBytes();
            Console.WriteLine("data的长度为:" + data.Length);
            //-------------------------------------------
            Room emptyRoom = new Room();
            MessageData.CreateMessage(data).Pakcet2Object(emptyRoom);
            
            //输出emptyRoom
            Console.WriteLine("roomName: "+emptyRoom.roomName);
            Console.WriteLine(emptyRoom.roomName+"的User");
            for (int i=0;i< emptyRoom.users.Count;i++) {
                Console.WriteLine(i+". name:"+ emptyRoom.users[i].name+
                    " age:"+ emptyRoom.users[i].age+" score:"+ emptyRoom.users[i].score);
                Console.Write(" interests:");
                for (int j = 0; j < emptyRoom.users[j].interests.Count; j++) {
                    Console.Write(emptyRoom.users[i].interests[j] + " ");
                }
                Console.WriteLine();
            }
            Console.ReadLine();
        }

    }

Room这个结构算是比较复杂的了,而这里依然是使用AddObject和Pakcet2Object这两个方法来完成数据交换。

到这里MessageData已经介绍完了,基于高效率的字节转换,MessageData能以十分轻小的体积完成数据交换,非常适合在网络通信频率高的情况小使用。同样的MessageData转换后的字节数据是没有可读性的,并且由于依赖于对象反射进行解析,所以无法进行动态数据解析,在一些情况下无法进行hotfix。

berry在一年前开发出了MessageData的第一个版本,经过长时间的使用和修改,berry在其基础上开发出了另一个交换格式MessageBundle。MessageBundle抛弃了与对象的依赖,通过数据模板实现了动态解析,berry会后续文章中继续介绍。

(5)

本文由 树莓屋 作者:Berry贝锐 发表,转载请注明来源!

关键词:

热评文章

评论:

1 条评论,访客:1 条,博主:0 条
  1. AlanWang
    AlanWang发布于: 

    能直接解析对象和list很赞

发表评论