Excel编程工具转换CShap
好的,我们继续上一章。
前一章我们已经把需要的数据缓存起来了,我们现在开始读表,把表中的数据拿出来缓存到对象里面去,所以,我们得需要一个对象下列代码:
[Serializable]
public class DataSvo
{
private Dictionary<string, Dictionary<string, List<Tuple<Type, string, string, object>>>> m_dataDictionary = new Dictionary<string, Dictionary<string, List<Tuple<Type, string, string, object>>>>();
public Dictionary<string, Dictionary<string, List<Tuple<Type, string, string, object>>>> DataDictionary { get => m_dataDictionary; set => m_dataDictionary = value; }
public void AddData(string className, Tuple<Type, string, string, object> data, string index)
{
string classFomatName = className;
if (!DataDictionary.ContainsKey(classFomatName))
{
Dictionary<string, List<Tuple<Type, string, string, object>>> m_data = new Dictionary<string, List<Tuple<Type, string, string, object>>>()
{
{ index, new List<Tuple<Type,string, string, object>>() { data } }
};
DataDictionary.Add(classFomatName, m_data);
}
else
{
if (DataDictionary[classFomatName].ContainsKey(index))
{
DataDictionary[classFomatName][index].Add(data);
}
else
{
DataDictionary[classFomatName].Add(index, new List<Tuple<Type, string, string, object>>() { data });
}
}
}
}
我们的对象里面包含一个字典,这个字典里面缓存将来以及现在需要的所有数据,以类名做Key,并且标记为可序列化。[Serializable]
OK,走到这里,我们开始看看 XlsxReadResult(InitSheetClass sheetClass)这个方法吧。这个方法就是开始读工作表,但是此时我们不需要读第一张sheet了,因为我们在前面已经缓存到了 InitSheetClass这个对象里面。
private void XlsxReadResult(InitSheetClass sheetClass)
{
//这边会把所有的信息全部读取出来,并且放进去字符串里面
//这里面包含所有的信息表格,一个文件的sheetClass
Dictionary<int, Tuple<string, string, string>> data_x_type = new Dictionary<int, Tuple<string, string, string>>();
if (sheetClass.Sheet_dic.Count > 0)
{
Dictionary<string, Tuple<bool, string>>.Enumerator tor = sheetClass.Sheet_dic.GetEnumerator();
int forStartRowCount = 4;
int forTypeStartRowCount = 1;
int forTypeNameStartRow = 3;
int forZhuShiStartRow = 2;
int forIndex = 0;
string className = "";
while (tor.MoveNext())
{
ISheet sheet = sheetClass.OperaterData.workBook.GetSheet(tor.Current.Key);//根据缓存下来的sheet来取对应的数据表
IRow row;
IRow row_type;
IRow row_type_name;
IRow row_type_ZhuShi;
className = sheetClass.ClassName[forIndex];
if (dataSvo.DataDictionary.ContainsKey(className + "Vo")) throw new Exception("存在相同的类!");
if (sheet != null)
{
data_x_type.Clear();
row_type = sheet.GetRow(forTypeStartRowCount);//先获取type行
row_type_name = sheet.GetRow(forTypeNameStartRow);//先获取type name行
row_type_ZhuShi = sheet.GetRow(forZhuShiStartRow);//先获取注释行
if (row_type != null && row_type_name != null)
{
Dictionary<int, string> m_str_Type_Name = new Dictionary<int, string>();
Dictionary<int, string> m_str_ZhuShi = new Dictionary<int, string>();
m_str_Type_Name.Clear();
m_str_ZhuShi.Clear();
int keyId = 0;
for (int i = 0; i < row_type_name.LastCellNum; i++)
{
string strValue = row_type_name.GetCell(i).ToString();
if (tor.Current.Value.Item2.Equals(strValue))
{
keyId = i;
}
if (!string.IsNullOrEmpty(strValue)) m_str_Type_Name.Add(i, strValue);
}
for (int i = 0; i < row_type_ZhuShi.LastCellNum; i++)
{
string strValue = row_type_ZhuShi.GetCell(i).ToString();
if (!string.IsNullOrEmpty(strValue)) m_str_ZhuShi.Add(i, strValue);
}
for (int i = row_type.FirstCellNum; i < row_type.LastCellNum; i++)
{
string strValue = row_type.GetCell(i).ToString();
if (!string.IsNullOrEmpty(strValue))
{
string typeInfoName = "";
string zhushi = "";
if (m_str_Type_Name.ContainsKey(i))
{
typeInfoName = m_str_Type_Name[i];
}
if (m_str_ZhuShi.ContainsKey(i))
{
zhushi = m_str_ZhuShi[i];
}
data_x_type.Add(i, new Tuple<string, string, string>(typeInfoName, zhushi, strValue));
}
}
if (data_x_type.Count > 0)
{
//循环每一行
int cacheIndex = 0;
for (int i = forStartRowCount; i <= sheet.LastRowNum; i++)
{
row = sheet.GetRow(i);
string cacheIdKey = "";
if (row != null)
{
//循环每一个格子
ICell cell;
for (int c = 0; c < row.LastCellNum; c++)
{
cell = row.GetCell(c);
if (tor.Current.Value.Item1 && c == keyId)
{
cacheIdKey = cacheIndex.ToString();
}
else if (!tor.Current.Value.Item1 && cacheIdKey == "")
{
cacheIdKey = cell.ToString();
}
if (data_x_type.ContainsKey(c))//只有定义了类型才会反序列去读取
{
string type_string = data_x_type[c].Item3.ToLowerInvariant();
Type typ = null;
//AddData(string className, Tuple<Type, object> data, int index) = Type.GetType("system."+type,true,true);
Tuple<Type, string, string, object> data;
object objParm = null;
switch (type_string.ToLowerInvariant())
{
case "int":
typ = typeof(int);
objParm = int.Parse(cell.ToString());
break;
case "string":
typ = typeof(string);
objParm = cell.ToString();
break;
case "array1|string":
typ = typeof(string[]);
//这里需要才分出来
string strData = cell.ToString();
objParm = strData.Split('|');
break;
case "uint":
typ = typeof(uint);
objParm = uint.Parse(cell.ToString());
break;
case "float":
typ = typeof(float);
objParm = float.Parse(cell.ToString());
break;
default:
break;
}
data = new Tuple<Type, string, string, object>(typ, data_x_type[c].Item1, data_x_type[c].Item2, objParm);
dataSvo.AddData(className, data, cacheIdKey);
}
}
cacheIndex++;
}
}
}
}
}
if(strGele != null) { strGele("正在序列化类:" + className); }
forIndex++;
}
}
if (updateProgressDelegate != null) { updateProgressDelegate(); }
}
上面的代码应该不难理解,我也没有整理过代码,就这样放出来了,最原始的思路也许是最容易理解的。
上面我会把前四行的数据取出来和有对应数据的格子 index。然后从指定的行开始遍历,包括格子,遍历完的数据全部塞入字典里面,放入我们可序列化的对象里面,做完这一切了,我们就可以把它去做二进制的序列化了。序列化需要用到 BinaryFormatter 这个对象,具体怎么去序列化就不用阐述了。然后我们还要写出CShap文件,下面代码:
private void WriteCShapFile()
{
if (dataSvo != null && dataSvo.DataDictionary.Count > 0)
{
FileStream fileStream = null;
string path = Directory.GetCurrentDirectory() + "/dataVo/";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
Dictionary<string, Dictionary<string, List<Tuple<Type, string, string, object>>>>.Enumerator data_All = dataSvo.DataDictionary.GetEnumerator();
StringBuilder stringBuilder = new StringBuilder();
while (data_All.MoveNext())
{
string className = data_All.Current.Key;
using (fileStream = new FileStream(path + className + ".cs", FileMode.Create))
{
Dictionary<string, List<Tuple<Type, string, string, object>>> child_data = data_All.Current.Value;
if (!string.IsNullOrEmpty(className) && child_data != null)
{
stringBuilder.Length = 0;
stringBuilder.AppendLine("using System;");
stringBuilder.AppendLine("[Serializable]");
stringBuilder.AppendLine("public class " + className + "\n{");
Dictionary<string, List<Tuple<Type, string, string, object>>>.Enumerator child_tor_data = child_data.GetEnumerator();
while (child_tor_data.MoveNext())
{
List<Tuple<Type, string, string, object>> list_local = child_tor_data.Current.Value;
for (int ty = 0; ty < list_local.Count; ty++)
{
stringBuilder.AppendLine(" private " + list_local[ty].Item1.FullName + " " + list_local[ty].Item2.ToLowerInvariant() + ";");
stringBuilder.AppendLine(" /// <summary> " + list_local[ty].Item3 + " </summary>");
stringBuilder.AppendLine(" public " + list_local[ty].Item1.FullName + " " + GetFirstUpperStr(list_local[ty].Item2) + "\n {");
stringBuilder.AppendLine(" get { return " + list_local[ty].Item2.ToLowerInvariant() + "; }");
stringBuilder.AppendLine(" set { " + list_local[ty].Item2.ToLowerInvariant() + " = value;}");
stringBuilder.AppendLine(" }");
}
break;//只写一次
}
stringBuilder.AppendLine("}");
}
string str = stringBuilder.ToString();
byte[] writeByte = Encoding.UTF8.GetBytes(str);
fileStream.Write(writeByte, 0, writeByte.Length);
fileStream.Flush();
}
if(strGele != null) { strGele("正在写出类:" + className); }
if(updateProgressDelegate != null) { updateProgressDelegate(); }
}
}
//这里开始写出二进制文件
}
OK,结束了,也不是很难,之前想在Unity编辑器实现的,不过Unity编辑器考虑到性能问题,卡顿严重,就使用了C#的窗口程序来实现了,不过我把它封装成DLL的类库了。
然后我们在Unity 里面通过C#反射来调用
public Dictionary<int,T> GetDataByClass<T>()
{
if (!DataSVOS.isDeserialize) { return null; }
Dictionary<int, T> result_dic = new Dictionary<int, T>();
Assembly ably = Assembly.GetExecutingAssembly();
Type type = typeof(T);
if (DataSVOS.MDataSvo.DataDictionary.ContainsKey(type.Name))
{
PropertyInfo[] propertyInfos = type.GetProperties();
Dictionary<string, PropertyInfo> property_dic = new Dictionary<string, PropertyInfo>();
for (int i = 0; i < propertyInfos.Length; i++)
{
property_dic[propertyInfos[i].Name.ToLowerInvariant()] = propertyInfos[i];
}
Dictionary<string, List<SerialzeData.Tuple<Type, string, string, object>>>.Enumerator data_tor = DataSVOS.MDataSvo.DataDictionary[type.Name].GetEnumerator();
while (data_tor.MoveNext())
{
object classObject = ably.CreateInstance(type.Name);//每次创建一个实例
for (int i = 0; i < data_tor.Current.Value.Count; i++)
{
string keyNe = data_tor.Current.Value[i].Item2.ToLowerInvariant();
if (property_dic.ContainsKey(keyNe))
{
PropertyInfo info = property_dic[keyNe];
info.SetValue(classObject, data_tor.Current.Value[i].Item4, null);
//if (info.PropertyType.Name == "String")
//{
// info.SetValue(classObject, data_tor.Current.Value[i].Item4, null);
//}
//else
//{
// //Type conver = data_tor.Current.Value[i].Item1;
// object converSucceedObj = Convert.ChangeType(data_tor.Current.Value[i].Item4, data_tor.Current.Value[i].Item1);
// Type conver = data_tor.Current.Value[i].Item1;
// MethodInfo methodInfo = conver.GetMethod("CompareTo", new Type[] { typeof(object) });//反射获取类型转换的parse方法
// if (methodInfo != null)
// {
// System.Object obj = Activator.CreateInstance(conver);//创建出数据类型
//
// BindingFlags flags = System.Reflection.BindingFlags.Public | BindingFlags.Static | System.Reflection.BindingFlags.Instance;
// if (data_tor.Current.Value[i].Item4 == null)
// {
// info.SetValue(classObject, null, null);
// }
// else
// {
// object[] parameters = new object[] { converSucceedObj };//把参数放进数组里面
// object result = methodInfo.Invoke(obj,flags,Type.DefaultBinder, parameters,null);
// Console.WriteLine(result);
// }
// }
// else
// {
// throw new Exception("get parse is null!");
// }
//}
}
}
result_dic.Add(int.Parse(data_tor.Current.Key), (T)classObject);
}
}
return result_dic;
}
当然这边最好在第一次启动的时候就把数据初始化完毕,然后就不会每次都反射调用反序列化数据了。
然后我们可以看到,数据是已经读取出来了。Game Over!