手工使用Protobuf-net工具来序列化对象

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/stalendp/article/details/55271146

如果用C#中BinaryFormatter的序列化方式,体积太大,因为序列化了好多类型信息。这个时候用protobuf是最好的选择。在Unity中,可以使用Protobuf-net。 下面是手工写proto的例子:

[ProtoContract]
public class Student {
    [ProtoMember(1)]
    public int id;
    [ProtoMember(2)]
    public int age;
    [ProtoMember(3)]
    public string name;
    [ProtoMember(4)]
    public List<string> friends = new List<string>();

    public Student() { } // 缺省构造函数一定要有,否则不能调用Deserialize

    public Student(int _id, int _age, string _name) {
        id = _id;
        age = _age;
        name = _name;
    }

    public new string ToString() {
        return string.Format("id: {0}, age: {1}, name: {2}, friends: {3}", id, age, name, string.Join(",", friends.ToArray()));
    }
}

这个类,还可以用缺省写法:

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Student {
    public int id;
    public int age;
    public string name;
    public List<string> friends = new List<string>();

    public Student() { } // 缺省构造函数一定要有,否则不能调用Deserialize

    public Student(int _id, int _age, string _name) {
        id = _id;
        age = _age;
        name = _name;
    }

    public new string ToString() {
        return string.Format("id: {0}, age: {1}, name: {2}, friends: {3}", id, age, name, string.Join(",", friends.ToArray()));
    }
}
调用

[Test]
public void protoTest() {
    using(MemoryStream mem = new MemoryStream()) {
        // 初始化  
        var st = new Student(1, 22, "harry");
        st.friends = new List<string>() { "a", "b", "c", "d" };

        // 序列化到内存  
        Serializer.Serialize(mem, st);  //RuntimeTypeModel.Default.Serialize(mem, st);

        // 模拟序列化到磁盘  
        var buff = mem.ToArray();
        string logStr = System.Convert.ToBase64String(buff);
        UnityEngine.Debug.Log(string.Format("size: {0}, {1}\r\n{2}", buff.Length, logStr, ArrayConverter.toBinary(buff)));

        // 模拟从磁盘序反列化到内存  
        var buff2 = System.Convert.FromBase64String(logStr);
        var mem2 = new MemoryStream(buff2);

        // 从内存反序列化成对象  
        Student st2 = Serializer.Deserialize<Student>(mem2); // RuntimeTypeModel.Default.Deserialize(mem2, null, typeof(Student)) as Student;
        UnityEngine.Debug.Log("old: " + st.ToString());
        UnityEngine.Debug.Log("new: " + st2.ToString());
    }
}

另外,把byte[]数组显示出来的方式有:

byte[] buff = mem.ToArray();
// 1. 表示成二进制
ArrayConverter.toBinary(buff);
// 2. 表示成六十四进制(方便保存到日志中):
System.Convert.ToBase64String(buff);
// 3. 反序列化
System.Convert.FromBase64String(logStr);


复杂一些的例子:

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Student {
    public int id;
    public int age;
    public string name;
    public List<Book> readings = new List<Book>();

    public Student() { } // 缺省构造函数一定要有,否则不能调用Deserialize

    public Student(int _id, int _age, string _name) {
        id = _id;
        age = _age;
        name = _name;
    }

    public new string ToString() {
        return string.Format("id: {0}, age: {1}, name: {2}, readings: [{3}]", id, age, name, string.Join(", ", readings.ConvertAll(b => b.ToString()).ToArray()));
    }
}

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Book {
    public int bookNo;
    public string name;
    public int page;
    public Book() {
    }

    public Book(int _no, string _name, int _page) {
        bookNo = _no;
        name = _name;
        page = _page;
    }

    public new string ToString() {
        return string.Format("<bookNo: {0}, name: {1}, page: {2}>", bookNo, name, page);
    }
}

// 调用
 [Test]
    public void protoTest() {
        using(MemoryStream mem = new MemoryStream()) {
            // 初始化  
            var st = new Student(1, 22, "harry");
            st.readings.Add(new Book(1, "book1", 120));
            st.readings.Add(new Book(2, "book2", 320));
            st.readings.Add(new Book(3, "book3", 550));

            // 序列化到内存  
            Serializer.Serialize(mem, st);  

            // 模拟序列化到磁盘  
            byte[] buff = mem.ToArray();
            string logStr = System.Convert.ToBase64String(buff);
            UnityEngine.Debug.Log(string.Format("size: {0}, {1}\r\n{2}", buff.Length, logStr, ArrayConverter.toBinary(buff)));

            // 模拟从磁盘序反列化到内存  
            var buff2 = System.Convert.FromBase64String(logStr);
            var mem2 = new MemoryStream(buff2);

            // 从内存反序列化成对象  
            Student st2 = Serializer.Deserialize<Student>(mem2); 
            UnityEngine.Debug.Log("old: " + st.ToString());
            UnityEngine.Debug.Log("new: " + st2.ToString());
        }
    }

更加复杂的例子:

using ProtoBuf;
using System.Collections.Generic;
using Game.Extensions;
using System.IO;
using System;
using System.Runtime.Serialization;

// 同步测试工具
public class SynDebug {
    public static void protoTest() {
        var st = new MyPlayer(1, 22, "harry");
        st.readings.Add(new Book(1, "book1", 120));
        st.readings.Add(new Book(2, "book2", 320));
        st.readings.Add(new Book(3, "book3", 550));
        st.ops.Add(new OP(1, 2, 3, 4, 5, 6, true, false, true));
        st.ops.Add(new OP(6, 5, 4, 3, 2, 11, false, true, false));
        var op = new OP(1, 2, 3, 4, 5, 6, true, false, true);
        testValid(st);
    }

    public static void test01() {
        DCardData cd = new DCardData(1, 2, true);
        testValid(cd);

        var player = createPlayer();
        testValid(player);

        var rd = createReplayData();
        testValid(rd);
    }

    public static ReplayData createReplayData() {
        ReplayData rd = new ReplayData(32111, createPlayer(1000), createPlayer(2000));
        return rd;
    }

    public static DPlayerData createPlayer(int id = 1000) {
        return new DPlayerData(new DCardData(id + 1, id + 1, true),
            new DCardData[] {
                new DCardData(id + 21, id + 21, true),
                new DCardData(id + 22, id + 22, false),
                new DCardData(id + 23, id + 23, true),
            }, new List<OP> {
                new OP(id + 31, 1, 1, 1, 331, 331, true, false, true),
                new OP(id + 32, 2, 2, 2, 332, 332, false, true, false),
            }, new MyGamePlayer(id + 111, "harry", 222, 333, 444));
    }

    //System.Convert.ToBase64String(buff2), ArrayConverter.toBinary(buff1)
    private static void testValid<T>(T objO) {
        using(MemoryStream mem = new MemoryStream()) {
            //// 序列化到内存  
            Serializer.Serialize(mem, objO);  //RuntimeTypeModel.Default.Serialize(mem, st);

            // 模拟序列化到磁盘  
            byte[] buff1 = mem.ToArray();
            string logStr = System.Convert.ToBase64String(buff1);

            // 模拟从磁盘序反列化到内存  
            var buff2 = System.Convert.FromBase64String(logStr);
            var mem2 = new MemoryStream(buff2);

            // 从内存反序列化成对象  
            T objN = Serializer.Deserialize<T>(mem2); // RuntimeTypeModel.Default.Deserialize(mem2, null, typeof(Student)) as Student;

            var bo = logStr;
            var bn = System.Convert.ToBase64String(buff2);
            var co = objO.ToString();
            var cn = objN.ToString();

            UnityEngine.Debug.LogFormat("isSame: {0}(b) {1}(c)\r\nbytes({2}) : {3}\r\n\r\nOLD:{4}\r\nNEW:{5}\r\n{6}",
                bo.Equals(bn), co.Equals(cn),
                buff1.Length, bo,
                co,
                cn,
                ArrayConverter.toBinary(buff1)
                );
        }
    }
}

[ProtoContract]
public class DPlayerData : BitField {
    [ProtoMember(1)]
    public DCardData hero;
    [ProtoMember(2)]
    public DCardData[] cards;
    [ProtoMember(3)]
    private List<byte[]> opDatas;
    [ProtoMember(4)]
    public int userid;
    [ProtoMember(5)]
    public int level;
    [ProtoMember(6)]
    public int pvpScore;
    [ProtoMember(7)]
    public int pvpLevel;
    [ProtoMember(8)]
    public string name;

    public List<OP> operations;

    public DPlayerData() { }

    public DPlayerData(DCardData _hero, DCardData[] _cards, List<OP> _operstions, MyGamePlayer p) {
        hero = _hero;
        cards = _cards;
        operations = _operstions;

        userid = p.ID;
        name = p.Name;
        level = p.Level;
        pvpScore = p.Score;
        pvpLevel = p.PvpLevel;
    }

    //public void fillInfo(List<Common.Hero> hs, List<long> ops, Server.UserSummary us) {
    //    System.Array.ForEach(cards, c => { hs.Add(c.toHero()); });
    //    System.Array.ForEach(operations, op => { ops.Add(op); });

    //    us.userid = userid;
    //    us.name = name;
    //    us.level = level;
    //    us.pvpScore = pvpScore;
    //    us.pvpLevel = pvpLevel;
    //}

    [OnSerializing]
    public void OnSerializing(StreamingContext context) {
        opDatas = new List<byte[]>();
        operations.ForEach(op => opDatas.Add(op.toArray()));
    }

    [OnDeserialized]
    public void OnDeserialized(StreamingContext context) {
        operations = new List<OP>();
        opDatas.ForEach(opds => operations.Add(new OP(opds)));
    }

    public override string ToString() {
        var cifs = string.Join(",", Array.ConvertAll(cards, c => c.ToString()));
        var opinfs = string.Join(",", Array.ConvertAll(operations.ToArray(), op => op.ToString()));
        return string.Format("hero: <{0}>, cards: <{1}>, ops: <{2}>, userid: {3}, level: {4}, pvpScore: {5}, pvpLevel: {6}, name: {7}", 
            hero.ToString(), cifs, opinfs, userid, level, pvpScore, pvpLevel, name);
    }
}

public class MyGamePlayer {
    public int ID;
    public string Name;
    public int Level;
    public int Score;
    public int PvpLevel;

    public MyGamePlayer(int _Id, string _Name, int _Level, int _Score, int _PvpLevel) {
        ID = _Id;
        Name = _Name;
        Level = _Level;
        Score = _Score;
        PvpLevel = _PvpLevel;
    }
}

// 卡牌类型数据
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class DCardData {
    public int id;
    public int level;
    public bool isHero;

    public DCardData() { }

    public DCardData(int _id, int _level, bool _isHero = false) {
        id = _id;
        level = _level;
        isHero = _isHero;
    }

    public Common.Hero toHero() {
        Common.Hero h = new Common.Hero();
        h.tid = id;
        h.level = level;
        h.isHero = isHero ? 1 : 0;
        return h;
    }

    public override string ToString() {
        return string.Format("id: {0}, level: {1}, isHero: {2}", id, level, isHero);
    }
}

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class ReplayData {
    public int randomSeed;
    public DateTime time;
    public DPlayerData home;
    public DPlayerData visit;

    public ReplayData() { }

    public ReplayData(int seed, DPlayerData _home, DPlayerData _visit) {
        time = DateTime.UtcNow;
        randomSeed = seed;
        home = _home;
        visit = _visit;
    }

    public override string ToString() {
        return string.Format("seed: {0}, home: <{1}>, visit: <{2}>", randomSeed, home.ToString(), visit.ToString());
    }
}

/**
 * Encode MsgFrameOp to Int64, FrameNo : 0~15, Others: 16~31, DeployCard.X: 32~47, DeployCard.Y: 48~63
 * 
 *  0           16         32         48      63 
 * ______________________________________________
 * |  FrameNo  |  Others  |    X     |     Y    |
 * ______________________________________________
 * 
 *  Other:
 *   16         20          24           28       31
 *  _________________________________________________
 *  |   Type   | Deploy ID | Select ID  |  Face ID  |
 *  _________________________________________________
 *  
 *  Type:
 *       16             17         18          19
 *  __________________________________________________
 *  | DeployCard |  SelectCard | MakeFace |  not use |
 *  __________________________________________________
 *  
 * */
public class OP : BitField {
    [BitInfo(16)]
    public int FrameNo;
    [BitInfo(1)]
    public bool isDeployCard;
    [BitInfo(1)]
    public bool isSelectCard;
    [BitInfo(1)]
    public bool isMakeFace;
    [BitInfo(1)]
    public bool NotUsed;
    [BitInfo(4)]
    public int DeployID;
    [BitInfo(4)]
    public int SelectID;
    [BitInfo(4)]
    public int FaceID;
    [BitInfo(16)]
    public int X;
    [BitInfo(16)]
    public int Y;

    public OP(int FrameNo, int DeployID, int SelectID, int FaceID, int X, int Y, bool isDeployCard, bool isSelectCard, bool isMakeFace) {
        this.FrameNo = FrameNo;
        
        this.DeployID = DeployID;
        this.SelectID = SelectID;
        this.FaceID = FaceID;
        this.X = X;
        this.Y = Y;

        this.isDeployCard = isDeployCard;
        this.isSelectCard = isSelectCard;
        this.isMakeFace = isMakeFace;
    }

    public OP(byte[] datas) {
        parse(datas);
    }

    public new string ToString() {
        return string.Format("<FrameNo: {0}, DeployID: {1}, SelectID: {2}, FaceID: {3}, X: {4}, Y: {5}, {6}, {7}, {8}>",
            FrameNo, DeployID, SelectID, FaceID, X, Y, isDeployCard, isSelectCard, isMakeFace);
    }
}

[ProtoContract(/*ImplicitFields = ImplicitFields.AllPublic*/)]
public class MyPlayer {
    [ProtoMember(1)]
    public int id;
    [ProtoMember(2)]
    public int age;
    [ProtoMember(3)]
    public string name;
    [ProtoMember(4)]
    public List<Book> readings = new List<Book>();
    [ProtoMember(5)]
    private List<byte[]> opData;

    public List<OP> ops = new List<OP>();

    public MyPlayer() { } // 缺省构造函数一定要有,否则不能调用Deserialize

    public MyPlayer(int _id, int _age, string _name) {
        id = _id;
        age = _age;
        name = _name;
    }

    [OnSerializing]
    public void OnSerializing(StreamingContext context) {
        opData = new List<byte[]>();
        ops.ForEach(op => opData.Add(op.toArray()));
    }

    [OnDeserialized]
    public void OnDeserialized(StreamingContext context) {
        ops.Clear();
        opData.ForEach(opds => ops.Add(new OP(opds)));
    }

    public override string ToString() {
        return string.Format("id: {0}, age: {1}, name: {2}, readings: [{3}], Ops: [{4}]", id, age, name,
            string.Join(", ", readings.ConvertAll(b => b.ToString()).ToArray()),
            string.Join(", ", ops.ConvertAll(op => op.ToString()).ToArray()));
    }
}

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Book {
    public int bookNo;
    public string name;
    public int page;
    public Book() {
    }

    public Book(int _no, string _name, int _page) {
        bookNo = _no;
        name = _name;
        page = _page;
    }

    public new string ToString() {
        return string.Format("<bookNo: {0}, name: {1}, page: {2}>", bookNo, name, page);
    }
}


参考:

http://stackoverflow.com/questions/29155659/why-is-binaryformatter-trying-to-serialize-too-much-data

http://stackoverflow.com/questions/675349/deserialize-unknown-type-with-protobuf-net

http://stackoverflow.com/questions/12308196/protobuf-net-serialization-without-annotation

猜你喜欢

转载自blog.csdn.net/stalendp/article/details/55271146