代码改变世界

.NET 2.0 JSON Parser

2013-06-14 23:30  无名365  阅读(395)  评论(0编辑  收藏  举报

A simple pure .Net 2.0 Library for JSON parser -- JSON-Sharp

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Collections;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;
using System.Collections.Specialized;
using System.Web.UI;


namespace JsonSharp
{
public enum JSONSerializationMode
{
/// <summary>
/// Slower, but takes advantage of overriden ToJSON methods
/// </summary>
UseReflection,
/// <summary>
/// Faster, but ignores overriden ToJSON methods
/// </summary>
NoReflection
};
/// <summary>
/// This class encodes and decodes JSON strings.
/// </summary>
public static class JSON
{
/// <summary>
/// Converts the public Properties of any object to a JSON-encoded string. Uses reflection to take advantaget of overridden ToJSON methods
/// </summary>
/// <returns>JSON-encoded string</returns>
public static string ToJSON(object obj)
{
if (obj == null)
return "null";

return ToJSON(obj, JSONSerializationMode.UseReflection);
}

public static string ToJSON(object obj, JSONSerializationMode mode)
{
if (obj == null)
return "null";

if (mode == JSONSerializationMode.UseReflection)
{
Type type = obj.GetType();
MethodInfo[] methods = type.GetMethods();
MethodInfo method = null;
foreach (var mi in methods)
{
if (mi.Name == "ToJSON" && mi.ReturnType == typeof(string) 
&& mi.DeclaringType == type && mi.GetParameters().Length == 0)
{
method = mi;
break;
}
}
//methods = methods.Where(c => c.Name == "ToJSON" && c.ReturnType == typeof(string)).ToArray();
//MethodInfo method = methods.SingleOrDefault(c => c.DeclaringType == type && c.GetParameters().Length == 0);

if (method != null)
return (string)method.Invoke(obj, new object[0]);
}

return JSON.Serialize(obj, mode);
}

public static string EscapeString(string obj)
{
return obj.Replace("\\", "\\\\").Replace("/", "\\/").Replace("\"", "\\\"")
.Replace("\b", "\\b").Replace("\f", "\\f").Replace("\n", "\\n")
.Replace("\r", "\\r").Replace("\t", "\\t");
}

public static string UnescapeString(string obj)
{
if (!obj.Contains("\\"))
return obj;

return obj.Replace("\\t", "\t").Replace("\\r", "\r").Replace("\\n", "\n")
.Replace("\\f", "\f").Replace("\\b", "\b").Replace("\\\"", "\"")
.Replace("\\/", "/").Replace("\\\\", "\\");

}

public static string Serialize(object obj)
{
return Serialize(obj, JSONSerializationMode.UseReflection);
}

public static string Serialize(object obj, JSONSerializationMode mode)
{
if (obj == null)
return "null";

Type type = obj.GetType();

if (IsNumeric(type))
{
return obj.ToString();
}
else if (type == typeof(string) || type == typeof(char))
{
return "\"" + EscapeString("" + obj) + "\"";
}
else if (type == typeof(bool))
{
bool x = (bool)obj;

return x ? "true" : "false";
}
else if (type == typeof(DateTime))
{
DateTime dt = (DateTime)obj;

return "\"" + dt.ToString("MM/dd/yyyy hh:mm:ss tt") + "\"";
}

StringBuilder result = new StringBuilder();

if (obj.GetType().IsArray)
{
result.Append("[");

Array array = (Array)obj;

foreach (object value in array)
{
result.Append(ToJSON(value,mode) + ",");
}

string json = result.ToString();

if (json.EndsWith(","))
json = json.Substring(0, json.Length - 1);

json += "]";

return json;
}
else if (obj is IDictionary)
{
IDictionary d = (IDictionary)obj;
IDictionaryEnumerator i = d.GetEnumerator();

result.Append("{");

while (i.MoveNext())
{
result.Append("\"" + i.Key + "\":");
result.Append(ToJSON(i.Value,mode));
result.Append(",");
}

string json = result.ToString();

if (json.EndsWith(","))
json = json.Substring(0, json.Length - 1);

json += "}";

return json;
}
else if (obj is NameValueCollection)
{
NameValueCollection col = (NameValueCollection)obj;

result.Append("{");

for (int i = 0; i < col.Count; i++)
{
string key = col.Keys[i];
string value = col[key];

result.Append("\"" + key + "\":");
result.Append(ToJSON(value,mode));
result.Append(",");
}

string json = result.ToString();

if (json.EndsWith(","))
json = json.Substring(0, json.Length - 1);

json += "}";

return json;
}
else if (obj is IEnumerable)
{
IEnumerable item = (IEnumerable)obj;
IEnumerator i = item.GetEnumerator();

result.Append("[");

while (i.MoveNext())
{
result.Append(ToJSON(i.Current,mode) + ",");
}

string json = result.ToString();

if (json.EndsWith(","))
json = json.Substring(0, json.Length - 1);

json += "]";

return json;

}
else
{
result.Append("{");

PropertyInfo[] props = obj.GetType().GetProperties();


foreach (PropertyInfo prop in props)
{
if (IgnoreProperty(prop))
continue;

object value = prop.GetValue(obj, null);

result.Append("\"" + prop.Name + "\":");
result.Append(ToJSON(prop.GetValue(obj, null),mode));
result.Append(",");
}

string json = result.ToString();

if (json.EndsWith(","))
json = json.Substring(0, json.Length - 1);

json += "}";

return json;
}
}

private static bool IgnoreProperty(PropertyInfo prop)
{
if (prop.GetIndexParameters().Length > 0)
return true;

string Namespace = prop.PropertyType.Namespace;

if (Namespace != null && (Namespace.StartsWith("System.Reflection") || Namespace.StartsWith("System.Security")))
return true;

Type[] ignoredTypes = new Type[] { typeof(Type), typeof(HtmlTextWriter), typeof(TextWriter), typeof(Stream) };

foreach (Type type in ignoredTypes)
{
if (prop.PropertyType == type || prop.PropertyType.IsSubclassOf(type))
return true;
}

return false;
}

/// <summary>
/// Parses a JSON string into the specified object type
/// </summary>
/// <param name="json">The JSON-encoded string you want to deserialize</param>
/// <param name="type">The object type you want your json string deserialized into</param>
/// <returns>Object of type Type</returns>
public static object Deserialize(string json, Type type)
{
object o = Deserialize(json);
PropertyCache.Clear();
ConstructorCache.Clear();
return Deserialize(o, type);
}

public static T Deserialize<T>(string json)
{
return (T)Deserialize(json, typeof(T));
}

private static Hashtable PropertyCache = new Hashtable();
private static Hashtable ConstructorCache = new Hashtable();
private static Hashtable PropCanWrite = new Hashtable();

private static object Deserialize(object obj, Type type)
{
if (obj == null)
return null;

Type t = obj.GetType();

if (t == typeof(ArrayList))
{
if (type.IsArray)
type = type.GetElementType();

ArrayList elems = new ArrayList();

foreach (object o in (ArrayList)obj)
{
if (o != null)
elems.Add(Deserialize(o, type));
}

return elems.ToArray(type);
}

if (t == typeof(Hashtable))
{
Hashtable hash = (Hashtable)obj;
PropertyInfo[] props;
ConstructorInfo construct;

if (PropertyCache.ContainsKey(type))
{
props = (PropertyInfo[])PropertyCache[type];
construct = (ConstructorInfo)ConstructorCache[type];
}
else
{
props = type.GetProperties();
PropertyCache[type] = props;

construct = type.GetConstructor(new Type[] { });
ConstructorCache[type] = construct;
}

object elem = construct.Invoke(new object[] { });

foreach (PropertyInfo prop in props)
{
if (!prop.CanWrite)
continue;

object temp = hash[prop.Name];

if (temp == null)
temp = hash[prop.Name.ToLower()];

if (temp == null)
temp = hash[char.ToLower(prop.Name[0]) + prop.Name.Substring(1)];

if (temp != null)
prop.SetValue(elem, Deserialize(temp, prop.PropertyType), null);
}

return elem;
}

if (obj is double)
{
double val = (double)obj;

if (type == typeof(int))
return (int)val;
else if (type == typeof(short))
return (short)val;
else if (type == typeof(decimal))
return (decimal)val;
else if (type == typeof(long))
return (long)val;
else if (type == typeof(double))
return (double)val;
else if (type == typeof(float))
return (float)val;
else if (type == typeof(byte))
return (byte)val;
else
return val;
}

if (type == typeof(bool))
{
return (bool)obj;
}

if (type == typeof(string))
return obj.ToString();

if (type == typeof(DateTime))
{
DateTime outdt;

if (DateTime.TryParse(obj.ToString(), out outdt))
return outdt;
else
return new DateTime(1, 1, 1);
}

if (type == typeof(object))
return obj;

return null;
}

private static double outDouble = 0;

public static object Deserialize(string json)
{
JSONDeserializer jd = new JSONDeserializer(json);
return jd.Deserialize();
}

//This is just a test
private class JSONTypeDeserializer
{
private string json;
private Type type;
private int index;

public JSONTypeDeserializer(string json, Type type)
{
this.json = json;
this.type = type;
}

public object Deserialize()
{
index = 0;

if (type.IsArray)
{
var t = type.GetElementType();
return ProcessArray(t);

}
else
{
return ProcessHash(type);
}
}

private object ProcessValue(Type type)
{
SkipWhitespace();

if (json[index] == '[')
{
if (json == "[]")
return new object[0];

return ProcessArray(type);
}

if (json[index] == '{')
{
if (json == "{}")
{
ConstructorInfo constructor = type.GetConstructor(new Type[0]);

if (constructor != null)
return constructor.Invoke(new object[0]);
else
return null;
}

return ProcessHash(type);
}

if (json[index] == '"')
{
string val = UnescapeString(ProcessString());

DateTime outDate;

if (DateTime.TryParse(val, out outDate))
{
return outDate;
}

return val;
}

int startIndex = index;

while (index < json.Length && json[index] != ',' && json[index] != '}' && json[index] != ']' && !Char.IsWhiteSpace(json[index]))
index++;

string jval = json.Substring(startIndex, index - startIndex).Trim();

if (jval == "true")
return true;

if (jval == "false")
return false;

if (jval == "null")
return null;

if (type == typeof(int))
return int.Parse(jval);
else if (type == typeof(short))
return short.Parse(jval);
else if (type == typeof(decimal))
return decimal.Parse(jval);
else if (type == typeof(long))
return long.Parse(jval);
else if (type == typeof(double))
return double.Parse(jval);
else if (type == typeof(float))
return float.Parse(jval);

return null;
}

private object[] ProcessArray(Type type)
{
index++;

List<object> list = new List<object>();

while (true)
{
SkipWhitespace();

list.Add(ProcessValue(type));

if (!MoveNext())
break;
}

return list.ToArray();
}

private object ProcessHash(Type type)
{
ConstructorInfo constructor = type.GetConstructor(new Type[0]);

object obj;

if (constructor != null)
obj = constructor.Invoke(new object[0]);
else
return null;

index++;

PropertyInfo[] props = type.GetProperties();

Type t;

while (true)
{
SkipWhitespace();

string key = ProcessHashKey().Trim();
SkipWhitespace();

//PropertyInfo prop = props.FirstOrDefault(c => c.Name == key);
PropertyInfo prop = null;
foreach (var p in props)
{
if (p.Name == key)
{
prop = p;
break;
}
}

if (prop == null)
{
//prop = props.FirstOrDefault(c => c.Name.ToLower() == key.ToLower());
foreach (var p in props)
{
if (p.Name.ToLower() == key.ToLower())
{
prop = p;
break;
}
}
}

if (prop == null)
t = typeof(object);
else
t = prop.PropertyType;

object value = ProcessValue(t);

prop.SetValue(obj, value, null);

if (!MoveNext())
break;
}

return obj;
}

private bool MoveNext()
{
while (index < json.Length && json[index] != ',' &&
json[index] != ']' && json[index] != '}')
index++;

if (index >= json.Length)
return false;

if (json[index] == ']' || json[index] == '}')
{
index++;
return false;
}

index++;

SkipWhitespace();

if (index >= json.Length)
return false;

return true;
}

private void SkipWhitespace()
{
while (index < json.Length && Char.IsWhiteSpace(json[index]))
index++;
}

private string ProcessHashKey()
{
int startIndex = index;
while (json[index++] != ':')
;
string result = json.Substring(startIndex, index - startIndex - 1).Trim();
result = ChopStart(result,"\"");
result = ChopEnd(result,"\"");
return result;
}

private string ProcessString()
{
int startIndex = index;
StringBuilder result = new StringBuilder();

while (true)
{
index++;

while (json[index] != '"')
{
result.Append(json[index]);
index++;
}

int j = index - 1;
int count = 0;

while (json[j] == '\\')
{
j--;
count++;
}

//if there are an even number of backslashes, 
//then they are all just backslashes and they aren't escaping the quote
//otherwise, the quote is being escaped and we need to keep searching for the close quote
if (count % 2 == 0)
break;
else
result.Append(json[index]);
}

return result.ToString();
}


}

private class JSONDeserializer
{
private string json;
private int index;

public JSONDeserializer(string JSON)
{
index = 0;
json = JSON;
}

public object Deserialize()
{
index = 0;
SkipWhitespace();
return ProcessValue();
}

private object ProcessValue()
{
if (json[index] == '[')
{
return ProcessArray();
}

if (json[index] == '{')
{
return ProcessHash();
}

if (json[index] == '"')
{
string val = UnescapeString(ProcessString());

//DateTime outDate;

//if (DateTime.TryParse(val, out outDate))
//{
// return outDate;
//}

return val;
}

int startIndex = index;

while (index < json.Length && json[index] != ',' && json[index] != '}' && json[index] != ']' && !Char.IsWhiteSpace(json[index]))
index++;

string jval = json.Substring(startIndex, index - startIndex);

if (jval == "true")
return true;

if (jval == "false")
return false;

if (jval == "null")
return null;

if (double.TryParse(jval, out outDouble))
return outDouble;

return null;
}

private ArrayList ProcessArray()
{
index++;

ArrayList list = new ArrayList();

SkipWhitespace();

if (json[index] == ']')
{
index++;
return list;
}

while (true)
{

list.Add(ProcessValue());

if (!MoveNext())
break;
}

return list;
}

private Hashtable ProcessHash()
{
index++;

Hashtable hash = new Hashtable();

SkipWhitespace();

if (json[index] == '}')
{
index++;
return hash;
}

while (true)
{
string key = ProcessHashKey();

SkipWhitespace();

object value = ProcessValue();

hash.Add(key, value);

if (!MoveNext())
break;
}

return hash;
}

private bool MoveNext()
{
while (index < json.Length && json[index] != ',' &&
json[index] != ']' && json[index] != '}')
index++;

if (index >= json.Length)
return false;

if (json[index] == ']' || json[index] == '}')
{
index++;
return false;
}

index++;

SkipWhitespaceEnd();

if (index >= json.Length)
return false;

return true;
}

private void SkipWhitespaceEnd()
{
while (index < json.Length && Char.IsWhiteSpace(json[index]))
index++;
}

private void SkipWhitespace()
{
while (Char.IsWhiteSpace(json[index]))
index++;
}

private string ProcessHashKey()
{
int startIndex = index + 1;
while (json[index++] != ':')
;
string result = json.Substring(startIndex, index - startIndex - 2).TrimEnd();
return result;
}

private string ProcessString()
{
int startIndex = index + 1;

while (true)
{
index++;

while (json[index] != '"')
index++;

int j = index - 1;
int count = 0;

while (json[j] == '\\')
{
j--;
count++;
}

//if there are an even number of backslashes, 
//then they are all just backslashes and they aren't escaping the quote
//otherwise, the quote is being escaped and we need to keep searching for the close quote
if (count % 2 == 0)
break;
}

return json.Substring(startIndex, index - startIndex);
}

}

private static string ChopStart(string str, string x)
{
if (str.StartsWith(x))
return str.Substring(x.Length);

return str;
}

private static string ChopEnd(string str, string x)
{
if (str.EndsWith(x))
return str.Substring(0, str.Length - x.Length);

return str;
}

private static bool IsNumeric(Type type)
{
Type[] types = new Type[]{
typeof(int), typeof(short), typeof(long),
typeof(double), typeof(decimal), typeof(byte)};

foreach (var t in types)
{
if (t == type)
{
return true;
}
}

return false;
//return types.Contains(type);
}


}
}