手工使用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 反序列化具有某些字段的派生类型的对象