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

Posted stalendp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手工使用Protobuf-net工具来序列化对象相关的知识,希望对你有一定的参考价值。

如果用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\\n2", 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\\n2", 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\\n6",
                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

以上是关于手工使用Protobuf-net工具来序列化对象的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能使用 ProtoBuf-Net 正确反序列化我的对象?

使用知道架构的 protobuf-net 反序列化未知对象

使用 protobuf-net 反序列化具有某些字段的派生类型的对象

关于在 C# 中使用 Protobuf-Net

使用 ProtoBuf-net 反序列化派生类型(字典)未正确设置对象字段

如何使用 protobuf-net 反序列化 .asmx webservice 中指定的对象