因为功能需求需要能够解析出继承父类的类的数据,没有找到这一类的json解析数据于是就想着自己写一个,下面来记录一下自己的解析过程。这个解析json的类主要可以解决父类的数据赋值问题,例如下面给出的例子。
/*父类*/
public abstract class TriggerDataParent : IListVoluation
{
/// <summary>
/// 对应原始数据的唯一id
/// </summary>
public float origId;
public string fullName;
}
/*继承TriggerDataParent 的类*/
public abstract class TriggerEventDataParent : TriggerDataParent
{
public TriggerEventDataParent(float origId) : base(origId) { }
public TriggerEventDataParent() { }
}
/*继承TriggerEventDataParent 类*/
public class TriggerEventItemTypeBehavior : TriggerEventDataParent
{
public float item_type;
public TriggerEventItemTypeBehavior() { }
public TriggerEventItemTypeBehavior(float origId) : base(origId) { }
}
//从以上可以看出类TriggerEventItemTypeBehavior 的父类为TriggerEventDataParent ,
//TriggerDataParent 。可以看出TriggerDataParent 中有两个变量,
//当存储为json数据后TriggerEventItemTypeBehavior 中会出现这两个变量,
//但是当解析该json数据时会出错,因为在TriggerEventItemTypeBehavior 中找不到这两个变量,
//我们写这个解析的初衷是为了解决这个问题,当然这个解析还有一些其他缺憾,
//希望各位大佬能够给出指点呀
首先分析一下json数据的格式,下面给出一段json数据
{"listChuFaQi":[
{"fullName":"DataStruct.ExportTriggerDataStruct","triggerStructIndex":1.0,
"listEvent":[
{"item_type":10100.0,"origId":2608.0,"fullName":"DataStruct.TriggerEventItemTypeBehavior"}
],
"listCondition":[
{"item_type":10100.0,"item_id":10020.0,"calculator_id":1003.0,"item_num":32.0,"origId":1101.0,"fullName":"DataStruct.TriggerConditionIntegerItem"}
],
"listAction":[
{"origId":0.0,"fullName":"DataStruct.TriggerActionNormalData"},
{"achievement_id":12.0,"origId":1500.0,"fullName":"DataStruct.TriggerActionReachChengJiu"}
]},
{"fullName":"DataStruct.ExportTriggerDataStruct","triggerStructIndex":6.0,
"listEvent":[
{"GM_str":"addEquip","origId":1004.0,"fullName":"DataStruct.TriggerEventPlayerGMStr"}
],
"listCondition":[],
"listAction":[
{"player_GM":1201.0,"origId":1003.0,"fullName":"DataStruct.TriggerActionPlayerChatCommand"}]
}],
"fullName":"DataStruct.ExportData"}
从上述数据中我们可以观察到"{"是一个类的开始“}”表示一个类的结束,"["表示一个List的开始"]"表示一个List的结束,等等还有其他的,于是我们一下定义:
private const int MARK_CLASS_OPEN = 0;//类的开始
private const int MARK_CALSS_CLOSE = 1;//类的结束
private const int MARK_LIST_OPEN = 2;//list的开始
private const int MARK_LIST_CLOSE = 3;//list的结束
private const int MARK_STRING = 4;//字符串的标识
/// <summary>
/// ","
/// </summary>
private const int MARK_COMMA = 5;
/// <summary>
/// 数字
/// </summary>
private const int MARK_NUMBER = 6;
private const int MARK_TRUE = 7;//bool值true的标识
private const int MARK_FALSE = 8;//bool值false的标识
private const int MARK_NULL = 9;//null数据的标识
定义了每一个标识我们可以根据传入的字符串(这里我们将字符串转化为char[]数组)来寻找下一个位置的标识是什么,代码如下:
//当前index将会指向实际位置
private int nextMark(char[] json, ref int index)
{
if (index == json.Length)//检查当前位置是否超出字符串长度
{
return WillJsonParse.MARK_NONE;
}
char c = json[index];
index++;
switch (c)
{
case '{':
return WillJsonParse.MARK_CLASS_OPEN;
case '}':
return WillJsonParse.MARK_CALSS_CLOSE;
case '[':
return WillJsonParse.MARK_LIST_OPEN;
case ']':
return WillJsonParse.MARK_LIST_CLOSE;
case '"':
return WillJsonParse.MARK_STRING;
case ',':
return WillJsonParse.MARK_COMMA;
case ':':
return WillJsonParse.MARK_COLON;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
return WillJsonParse.MARK_NUMBER;
}
index--;
int remainLength = json.Length - index;
if (remainLength >= 5)
{
if (json[index] == 'f' &&
json[index + 1] == 'a' &&
json[index + 2] == 'l' &&
json[index + 3] == 's' &&
json[index + 4] == 'e')
{
index += 5;
return WillJsonParse.MARK_FALSE;
}
}
if (remainLength >= 4)
{
if (json[index] == 't' &&
json[index + 1] == 'r' &&
json[index + 2] == 'u' &&
json[index + 3] == 'e')
{
index += 4;
return WillJsonParse.MARK_TRUE;
}
}
if (remainLength >= 4)
{
if (json[index] == 'n' &&
json[index + 1] == 'u' &&
json[index + 2] == 'l' &&
json[index + 3] == 'l')
{
index += 4;
return WillJsonParse.MARK_NULL;
}
}
return WillJsonParse.MARK_NONE;
}
//当前index不变
private int GetAhead(char[] json, int index)
{
int saveIndex = index;
return nextMark(json, ref saveIndex);
}
有了当前的标识我们可以根据标识来解析各个数据,下面给出一个方法用于解析数据的中转:
private object ParseValue(char[] json, ref int index, ref bool success)
{
switch (GetAhead(json, index))
{
case WillJsonParse.MARK_STRING:
return parseString(json, ref index);
case WillJsonParse.MARK_CLASS_OPEN:
return parseClass(json, ref index);
case WillJsonParse.MARK_LIST_OPEN:
return parseList(json, ref index);
case WillJsonParse.MARK_NUMBER:
return parseNumber(json, ref index);
case WillJsonParse.MARK_TRUE:
nextMark(json, ref index);
return Boolean.Parse("TRUE");
case WillJsonParse.MARK_FALSE:
nextMark(json, ref index);
return Boolean.Parse("FALSE");
case WillJsonParse.MARK_NULL:
nextMark(json, ref index);
return null;
case WillJsonParse.MARK_NONE:
break;
}
success = false;
return null;
}
下面就上面涉及到的方法逐一解释,首先看解析字符串
private string parseString(char[] json, ref int index)
{
string s = "";
char c;
//"--将第一个"去掉
c = json[index++];
bool complete = false;
while (!complete)
{
//已经到最后一个字符
if (index == json.Length)
{
complete = true;
break;
}
c = json[index++];
if (c == '"')
{
complete = true;
break;
}
else /*if ()*/
{
s += c;
}
}
return s;
}
再来看解析List数据,代码如下:
private List<object> parseList(char[] json, ref int index)
{
List<object> list = new List<object>();
nextMark(json, ref index);//去掉“[”
bool done = false;
while (!done)
{
int mark = GetAhead(json, index);//获取下一个标识
if (mark == WillJsonParse.MARK_NONE)
{
return null;
}
else if (mark == WillJsonParse.MARK_COMMA)//list中的一个元素结束
{
nextMark(json, ref index);
}
else if (mark == WillJsonParse.MARK_LIST_CLOSE)//list结束符
{
nextMark(json, ref index);
done = true;
break;
}
else
{
bool success = true;
object value = ParseValue(json, ref index, ref success);
if (!success)
{
return null;
}
list.Add(value);//添加value
}
}
return list;
}
下面看关于数字的解析,代码如下:
private float parseNumber(char[] json, ref int index)
{
int lastIndex = getLastIndexOfNumber(json, index);
int charLength = (lastIndex - index) + 1;
char[] numberCharArray = new char[charLength];
Array.Copy(json, index, numberCharArray, 0, charLength);
index = lastIndex + 1;
//可以看出这里统一转化为float类型,显然并不符合广泛应用
//这里需要改进能够用于得到int/double/long等类型
float f_vlaue = float.Parse(new string(numberCharArray));
return f_vlaue;
}
private int getLastIndexOfNumber(char[] json, int index)
{
int lastIndex;
for (lastIndex = index; lastIndex < json.Length; lastIndex++)
{
if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1)
{
break;
}
}
return lastIndex - 1;
}
最后来解析一个类,代码如下:
private Assembly assembly;
private object parseClass(char[] json, ref int index)
{
Hashtable table = new Hashtable();
int mark;
object clazz = null;
//{ 去掉{
nextMark(json, ref index);
bool done = false;
while (!done)
{
mark = GetAhead(json, index);
if (mark == WillJsonParse.MARK_NONE)
{
//数据格式不符合,返回null值
return null;
}
else if (mark == WillJsonParse.MARK_COMMA)
{//遇到“,”跳过该符号读取下一元素
nextMark(json, ref index);
}
else if (mark == WillJsonParse.MARK_CALSS_CLOSE)
{//遇到类结束符“}”该类结束返回该类
nextMark(json, ref index);//去掉“}”
clazz = creatClazz(table);
return clazz;
}
else
{
string name = parseString(json, ref index);
if (string.IsNullOrEmpty(name))
{//没有变量名数据出错
return null;
}
mark = nextMark(json, ref index);
if (mark != WillJsonParse.MARK_COLON)
{若不是冒号,数据出错
return null;
}
//值
bool success = true;
object value = ParseValue(json, ref index, ref success);
if (!success)
{//没有值,不符合标准,该类出错返回null
return null;
}
table[name] = value;
}
}
clazz = creatClazz(table);
return clazz;
}
//创建类的方法
private object creatClazz(Hashtable table)
{
if (assembly == null)
{
assembly = Assembly.GetExecutingAssembly();
}
//为了定位到每一个准确的类给每一个类定义了一个fullName变量,该变量赋值的内容为该类的完全限定名
//这里显然是一个并符合广泛的应用,还有需要改进的地方,小W想到的是自己写一个json的序列化额外定义
//每一个类的标识,当然这个序列化小W并没有写 @_@
if (table.ContainsKey("fullName"))
{
string fullName = table["fullName"].ToString();
object clazz = null;
Type type = assembly.GetType(fullName);
clazz = Activator.CreateInstance(type);
foreach (string tb in table.Keys)
{
object obj = table[tb];
if (obj is List<object>)
{
List<object> list = (List<object>)obj;
//这里关于List的类型的转化小编并没有写,只是给出了一个接口用来实现一个方法listVoluation
//用来自己将object类型转化为自己需要的类型
//显然这里也需要改进,小W这里是针对自己写的一个编辑器来写的解析,所以也就没有改变这个
MethodInfo method = clazz.GetType().GetMethod("listVoluation");
method.Invoke(clazz, new object[] { list });
}
else
{
try
{
clazz.GetType().GetField(tb).SetValue(clazz, obj);
}
catch (Exception e)
{
Debug.LogError("对字段设置出错提示:tb=" + tb + ",obj=" + obj.ToString() + "。" + e.Message);
}
}
}
return clazz;
}
return null;
}
最后我们在给出一个public方法用于外部调用反序列化,代码如下:
public object JsonDeserialize(string json)
{
object obj = null;
char[] charJson = json.ToCharArray();
int index = 0;
bool success = true;
obj = ParseValue(charJson, ref index, ref success);
return obj;
}
至此该解析已完成,从上述叙述中可以看出有三个已知的不足之处,以后若有需要小W会改进哒。