您的位置:首页 > 其它

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

2017-02-16 19:39 645 查看
如果用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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: