需求
需求内容
还是上一篇公司那个java转C#的需求,其中socket是json格式的。需求中的这个json需要能够动态增删json数据中的元素:每条数据的key和value都是动态定义的,解析出来才知道收到哪些key和value,而且有些value本身又是json数据,也就是说json数据嵌套。
需求并不复杂,但是找了找并没有找到好的json序列化和反序列化工具,.NET自带的运行时序列化和反序列化json工具(System.Runtime.Serialization.Json;)转换出来的结果并不符合要求。想来想去还是自己写一个,权当练手。
需求分析
既然不依靠插件就得手动分析字符串进行逐字符拆分了。这让我想起了计算机对数学运算表达式进行运算优先级解析并得出运算结果的例子。具体的实现过程已经忘了,大致记得是使用堆栈来对数据和运算符的顺序进行分离保存和运算直至最终结果。json文件虽然没有太多的优先级,但是{},“”,:以及反斜杠和逗号都对数据进行了分类和层级处理。
我们来看看这个json的字符串格式:
{"head":"setValue","info":"{\"value\":\"60\",\"key\":\"3DDepth\"}"}
从以上格式可以看出:
- 每个‘{’都有一个对应的‘}’,左右括号之间的数据可以看做一个json数据块。于是我们可以用Stack来处理:遇到‘{’进栈,遇到‘}’出栈,直至Stack中的元素数量为0,这样开从开始的‘{’到结束的‘}’之间的数据就是一个json数据块
- ‘,’分隔json键值对
- ‘:’分隔每个键值对的“Key“和”Value“
- ‘\’作为转义字符,读取的时候忽略,输出的时候加在内层json的符号前
- 所有的键值对使用队列存储起来,然后遇到‘:‘放出一个键,遇到’,‘或者json块结尾放出一个值直至结束
实现
废话不多说,直接上代码,代码比较简单,根据上述分析自己体会。
public class Json
{
public Json() { }
public Json(string firstname, object lastname)
{
this.FirstName = firstname;
this.LastName = lastname;
}
public string FirstName { get; set; }
public object LastName { get; set; }
}
public class JSONHelper
{
public static List<Json> Serialize(string str, Queue<object> quStr=null)
{
if (quStr == null) {
string[] sArray = str.Split(new char[6] { '{', '}', ',', ':', '\"', '\\' }, StringSplitOptions.RemoveEmptyEntries);
quStr = new Queue<object>();
for (int i = 0; i < sArray.Length; i++)
if (sArray[i] != "\"")
{
int a;
if (int.TryParse(sArray[i], out a))
quStr.Enqueue(a);
else
quStr.Enqueue(sArray[i]);
}
}
List<Json> json = new List<Json>();
json.Add(new Json());
for (int i = 0; i < str.Length; i++)
{
switch (str[i])
{
case '{':
Stack<char> parentheses = new Stack<char>();
parentheses.Push('{');
int j = i;
while (parentheses.Count > 0)//找到与之匹配的反括号的位置
{
j++;
if (str[j] == '}')
parentheses.Pop();
if (str[j] == '{')
parentheses.Push('{');
}
if (json[json.Count - 1].FirstName == null)
json = Serialize(str.Substring(i + 1, j - i - 1), quStr);
else
json[json.Count - 1].LastName = Serialize(str.Substring(i + 1, j - i - 1), quStr);//解析括号里的json数据
i = j;
break;
case ',':
json[json.Count - 1].LastName = quStr.Dequeue();
json.Add(new Json());
continue;
case ':':
json[json.Count - 1].FirstName = quStr.Dequeue().ToString();
continue;
case '\\': continue;
case '\"': continue;
default: continue;
}
}
if (quStr.Count > 0)
json[json.Count - 1].LastName = quStr.Dequeue();
return json;
}
public static string DeSerialize(List<Json> json, string mark = "\"")
{
if (json == null)
return null;
Queue<object> quStr = new Queue<object>();
quStr.Enqueue("{");
for (int i = 0; i < json.Count; i++)
{
quStr.Enqueue(mark + json[i].FirstName + mark);
quStr.Enqueue(":");
if (json[i].LastName.GetType() == json.GetType())
quStr.Enqueue(string.Format("{0}{1}{2}", mark, DeSerialize(json[i].LastName as List<Json>, "\\\""), mark));//解析json子节点的内容
else if (json[i].LastName.GetType() == (":").GetType())
quStr.Enqueue(mark + json[i].LastName + mark);
else
quStr.Enqueue((int)json[i].LastName);
if (i < json.Count - 1)
quStr.Enqueue(",");
}
quStr.Enqueue("}");
object[] sarr = new object[quStr.Count];
for (int j = 0; j < sarr.Length; j++)
sarr[j] = quStr.Dequeue();
return string.Join("", sarr);
}
}
转载请注明出处:http://blog.youkuaiyun.com/ylbs110/article/details/51280370
测试
测试代码
string str = "{\"head\":\"setValue\",\"info\":\"{\"value\":10,\"key\":\"3DDepth\"}\"}";
List<Json> json = JSONHelper.Serialize(str);
foreach (Json s in json){
Console.WriteLine(s.FirstName);
if (s.LastName.GetType() == json.GetType())
{
List<Json> jsons = s.LastName as List<Json>;
foreach (Json ss in jsons)
{
Console.WriteLine(" " + ss.FirstName);
Console.WriteLine(" " + ss.LastName);
}
}
else
Console.WriteLine(s.LastName);
}
Console.WriteLine(JSONHelper.DeSerialize(json));
输出
head
setValue
info
value
10
key
3DDepth
{"head":"setValue","info":"{\"value\":10,\"key\":\"3DDepth\"}"}