using System; using System.Collections; using System.Collections.Generic; #if !SILVERLIGHT using System.Data; #endif using System.Globalization; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Collections.Specialized; namespace FastJSON { public delegate string Serialize(object data); public delegate object Deserialize(string data); public sealed class JSONParameters { /// /// Use the optimized fast Dataset Schema format (default = True) /// public bool UseOptimizedDatasetSchema = true; /// /// Use the fast GUID format (default = True) /// public bool UseFastGuid = true; /// /// Serialize null values to the output (default = True) /// public bool SerializeNullValues = true; /// /// Use the UTC date format (default = True) /// public bool UseUTCDateTime = true; /// /// Show the readonly properties of types in the output (default = False) /// public bool ShowReadOnlyProperties = false; /// /// Use the $types extension to optimise the output json (default = True) /// public bool UsingGlobalTypes = true; /// /// Ignore case when processing json and deserializing /// [Obsolete("Not needed anymore and will always match")] public bool IgnoreCaseOnDeserialize = false; /// /// Anonymous types have read only properties /// public bool EnableAnonymousTypes = false; /// /// Enable fastJSON extensions $types, $type, $map (default = True) /// public bool UseExtensions = true; /// /// Use escaped unicode i.e. \uXXXX format for non ASCII characters (default = True) /// public bool UseEscapedUnicode = false; /// /// Output string key dictionaries as "k"/"v" format (default = False) /// public bool KVStyleStringDictionary = false; /// /// Output Enum values instead of names (default = False) /// public bool UseValuesOfEnums = false; /// /// Ignore attributes to check for (default : XmlIgnoreAttribute) /// public List IgnoreAttributes = new List { typeof(System.Xml.Serialization.XmlIgnoreAttribute) }; /// /// If you have parametric and no default constructor for you classes (default = False) /// /// IMPORTANT NOTE : If True then all initial values within the class will be ignored and will be not set /// public bool ParametricConstructorOverride = false; /// /// Serialize DateTime milliseconds i.e. yyyy-MM-dd HH:mm:ss.nnn (default = false) /// public bool DateTimeMilliseconds = false; /// /// Maximum depth for circular references in inline mode (default = 20) /// public byte SerializerMaxDepth = 20; /// /// Inline circular or already seen objects instead of replacement with $i (default = False) /// public bool InlineCircularReferences = false; /// /// Save property/field names as lowercase (default = false) /// public bool SerializeToLowerCaseNames = false; /// /// Serialize property name with underline style /// public bool UseApiNamingStyle = false; public void FixValues() { if (UseExtensions == false) // disable conflicting params { UsingGlobalTypes = false; InlineCircularReferences = true; } if (EnableAnonymousTypes) ShowReadOnlyProperties = true; } } public static class JSON { /// /// Globally set-able parameters for controlling the serializer /// public static JSONParameters Parameters = new JSONParameters(); /// /// Create a formatted json string (beautified) from an object /// /// /// /// public static string ToNiceJSON(object obj, JSONParameters param) { string s = ToJSON(obj, param); return Beautify(s); } /// /// Create a json representation for an object /// /// /// public static string ToJSON(object obj) { return ToJSON(obj, JSON.Parameters); } /// /// Create a json representation for an object with parameter override on this call /// /// /// /// public static string ToJSON(object obj, JSONParameters param) { param.FixValues(); Type t = null; if (obj == null) return "null"; if (obj.GetType().IsGenericType) t = Reflection.Instance.GetGenericTypeDefinition(obj.GetType()); if (t == typeof(Dictionary<,>) || t == typeof(List<>)) param.UsingGlobalTypes = false; // FEATURE : enable extensions when you can deserialize anon types if (param.EnableAnonymousTypes) { param.UseExtensions = false; param.UsingGlobalTypes = false; } return new JSONSerializer(param).ConvertToJSON(obj); } /// /// Parse a json string and generate a Dictionary<string,object> or List<object> structure /// /// /// public static object Parse(string json) { return new JsonParser(json).Decode(); } #if net4 /// /// Create a .net4 dynamic object from the json string /// /// /// public static dynamic ToDynamic(string json) { return new DynamicJson(json); } #endif /// /// Create a typed generic object from the json /// /// /// /// public static T ToObject(string json) { return new deserializer(Parameters).ToObject(json); } /// /// Create a typed generic object from the json with parameter override on this call /// /// /// /// /// public static T ToObject(string json, JSONParameters param) { return new deserializer(param).ToObject(json); } /// /// Create an object from the json /// /// /// public static object ToObject(string json) { return new deserializer(Parameters).ToObject(json, null); } /// /// Create an object from the json with parameter override on this call /// /// /// /// public static object ToObject(string json, JSONParameters param) { return new deserializer(param).ToObject(json, null); } /// /// Create an object of type from the json /// /// /// /// public static object ToObject(string json, Type type) { return new deserializer(Parameters).ToObject(json, type); } /// /// Fill a given object with the json represenation /// /// /// /// public static object FillObject(object input, string json) { Dictionary ht = new JsonParser(json).Decode() as Dictionary; if (ht == null) return null; return new deserializer(Parameters).ParseDictionary(ht, null, input.GetType(), input); } /// /// Deep copy an object i.e. clone to a new object /// /// /// public static object DeepCopy(object obj) { return new deserializer(Parameters).ToObject(ToJSON(obj)); } /// /// /// /// /// /// public static T DeepCopy(T obj) { return new deserializer(Parameters).ToObject(ToJSON(obj)); } /// /// Create a human readable string from the json /// /// /// public static string Beautify(string input) { return Formatter.PrettyPrint(input); } /// /// Register custom type handlers for your own types not natively handled by fastJSON /// /// /// /// public static void RegisterCustomType(Type type, Serialize serializer, Deserialize deserializer) { Reflection.Instance.RegisterCustomType(type, serializer, deserializer); } /// /// Clear the internal reflection cache so you can start from new (you will loose performance) /// public static void ClearReflectionCache() { Reflection.Instance.ClearReflectionCache(); } internal static long CreateLong(out long num, string s, int index, int count) { num = 0; bool neg = false; for (int x = 0; x < count; x++, index++) { char cc = s[index]; if (cc == '-') neg = true; else if (cc == '+') neg = false; else { num *= 10; num += (int)(cc - '0'); } } if (neg) num = -num; return num; } } internal class deserializer { public deserializer(JSONParameters param) { _params = param; } private JSONParameters _params; private bool _usingglobals = false; private Dictionary _circobj = new Dictionary(); private Dictionary _cirrev = new Dictionary(); public T ToObject(string json) { Type t = typeof(T); var o = ToObject(json, t); if (t.IsArray) { if ((o as ICollection).Count == 0) // edge case for "[]" -> T[] { Type tt = t.GetElementType(); object oo = Array.CreateInstance(tt, 0); return (T)oo; } else return (T)o; } else return (T)o; } public object ToObject(string json) { return ToObject(json, null); } public object ToObject(string json, Type type) { //_params = Parameters; _params.FixValues(); Type t = null; if (type != null && type.IsGenericType) t = Reflection.Instance.GetGenericTypeDefinition(type); if (t == typeof(Dictionary<,>) || t == typeof(List<>)) _params.UsingGlobalTypes = false; _usingglobals = _params.UsingGlobalTypes; object o = new JsonParser(json).Decode(); if (o == null) return null; #if !SILVERLIGHT if (type != null && type == typeof(DataSet)) return CreateDataset(o as Dictionary, null); else if (type != null && type == typeof(DataTable)) return CreateDataTable(o as Dictionary, null); #endif if (o is IDictionary) { if (type != null && t == typeof(Dictionary<,>)) // deserialize a dictionary return RootDictionary(o, type); else // deserialize an object return ParseDictionary(o as Dictionary, null, type, null); } else if (o is List) { if (type != null && t == typeof(Dictionary<,>)) // kv format return RootDictionary(o, type); else if (type != null && t == typeof(List<>)) // deserialize to generic list return RootList(o, type); else if (type == typeof(Hashtable)) return RootHashTable((List)o); else return (o as List).ToArray(); } else if (type != null && o.GetType() != type) return ChangeType(o, type); return o; } #region [ p r i v a t e m e t h o d s ] private object RootHashTable(List o) { Hashtable h = new Hashtable(); foreach (Dictionary values in o) { object key = values["k"]; object val = values["v"]; if (key is Dictionary) key = ParseDictionary((Dictionary)key, null, typeof(object), null); if (val is Dictionary) val = ParseDictionary((Dictionary)val, null, typeof(object), null); h.Add(key, val); } return h; } private object ChangeType(object value, Type conversionType) { if (conversionType == typeof(int)) return (int)((long)value); else if (conversionType == typeof(long)) return (long)value; else if (conversionType == typeof(string)) return (string)value; else if (conversionType.IsEnum) return CreateEnum(conversionType, value); else if (conversionType == typeof(DateTime)) return CreateDateTime((string)value); else if (Reflection.Instance.IsTypeRegistered(conversionType)) return Reflection.Instance.CreateCustom((string)value, conversionType); // 8-30-2014 - James Brooks - Added code for nullable types. if (IsNullable(conversionType)) { if (value == null) { return value; } conversionType = UnderlyingTypeOf(conversionType); } // 8-30-2014 - James Brooks - Nullable Guid is a special case so it was moved after the "IsNullable" check. if (conversionType == typeof(Guid)) return CreateGuid((string)value); return Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture); } private bool IsNullable(Type t) { if (!t.IsGenericType) return false; Type g = t.GetGenericTypeDefinition(); return (g.Equals(typeof(Nullable<>))); } private Type UnderlyingTypeOf(Type t) { return t.GetGenericArguments()[0]; } private object RootList(object parse, Type type) { Type[] gtypes = Reflection.Instance.GetGenericArguments(type); IList o = (IList)Reflection.Instance.FastCreateInstance(type); foreach (var k in (IList)parse) { _usingglobals = false; object v = k; if (k is Dictionary) v = ParseDictionary(k as Dictionary, null, gtypes[0], null); else v = ChangeType(k, gtypes[0]); o.Add(v); } return o; } private object RootDictionary(object parse, Type type) { Type[] gtypes = Reflection.Instance.GetGenericArguments(type); Type t1 = null; Type t2 = null; if (gtypes != null) { t1 = gtypes[0]; t2 = gtypes[1]; } if (parse is Dictionary) { IDictionary o = (IDictionary)Reflection.Instance.FastCreateInstance(type); foreach (var kv in (Dictionary)parse) { object v; object k = ChangeType(kv.Key, t1); if (kv.Value is Dictionary) v = ParseDictionary(kv.Value as Dictionary, null, t2, null); else if (t2.IsArray) v = CreateArray((List)kv.Value, t2, t2.GetElementType(), null); else if (kv.Value is IList) v = CreateGenericList((List)kv.Value, t2, t1, null); else v = ChangeType(kv.Value, t2); o.Add(k, v); } return o; } if (parse is List) return CreateDictionary(parse as List, type, gtypes, null); return null; } internal object ParseDictionary(Dictionary d, Dictionary globaltypes, Type type, object input) { object tn = ""; if (type == typeof(NameValueCollection)) return CreateNV(d); if (type == typeof(StringDictionary)) return CreateSD(d); if (d.TryGetValue("$i", out tn)) { object v = null; _cirrev.TryGetValue((int)(long)tn, out v); return v; } if (d.TryGetValue("$types", out tn)) { _usingglobals = true; globaltypes = new Dictionary(); foreach (var kv in (Dictionary)tn) { globaltypes.Add((string)kv.Value, kv.Key); } } bool found = d.TryGetValue("$type", out tn); #if !SILVERLIGHT if (found == false && type == typeof(System.Object)) { return d; // CreateDataset(d, globaltypes); } #endif if (found) { if (_usingglobals) { object tname = ""; if (globaltypes != null && globaltypes.TryGetValue((string)tn, out tname)) tn = tname; } type = Reflection.Instance.GetTypeFromCache((string)tn); } if (type == null) throw new Exception("Cannot determine type"); string typename = type.FullName; object o = input; if (o == null) { if (_params.ParametricConstructorOverride) o = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type); else o = Reflection.Instance.FastCreateInstance(type); } int circount = 0; if (_circobj.TryGetValue(o, out circount) == false) { circount = _circobj.Count + 1; _circobj.Add(o, circount); _cirrev.Add(circount, o); } Dictionary props = Reflection.Instance.Getproperties(type, typename, Reflection.Instance.IsTypeRegistered(type)); foreach (var kv in d) { var n = kv.Key; var v = kv.Value; string name = n.ToLower(); if (name == "$map") { ProcessMap(o, props, (Dictionary)d[name]); continue; } myPropInfo pi; if (props.TryGetValue(name, out pi) == false) continue; if (pi.CanWrite) { //object v = d[n]; if (v != null) { object oset = null; switch (pi.Type) { case myPropInfoType.Int: oset = (int)((long)v); break; case myPropInfoType.Long: oset = (long)v; break; case myPropInfoType.String: oset = (string)v; break; case myPropInfoType.Bool: oset = (bool)v; break; case myPropInfoType.DateTime: oset = CreateDateTime((string)v); break; case myPropInfoType.Enum: oset = CreateEnum(pi.pt, v); break; case myPropInfoType.Guid: oset = CreateGuid((string)v); break; case myPropInfoType.Array: if (!pi.IsValueType) oset = CreateArray((List)v, pi.pt, pi.bt, globaltypes); // what about 'else'? break; case myPropInfoType.ByteArray: oset = Convert.FromBase64String((string)v); break; #if !SILVERLIGHT case myPropInfoType.DataSet: oset = CreateDataset((Dictionary)v, globaltypes); break; case myPropInfoType.DataTable: oset = CreateDataTable((Dictionary)v, globaltypes); break; case myPropInfoType.Hashtable: // same case as Dictionary #endif case myPropInfoType.Dictionary: oset = CreateDictionary((List)v, pi.pt, pi.GenericTypes, globaltypes); break; case myPropInfoType.StringKeyDictionary: oset = CreateStringKeyDictionary((Dictionary)v, pi.pt, pi.GenericTypes, globaltypes); break; case myPropInfoType.NameValue: oset = CreateNV((Dictionary)v); break; case myPropInfoType.StringDictionary: oset = CreateSD((Dictionary)v); break; case myPropInfoType.Custom: oset = Reflection.Instance.CreateCustom((string)v, pi.pt); break; default: { if (pi.IsGenericType && pi.IsValueType == false && v is List) oset = CreateGenericList((List)v, pi.pt, pi.bt, globaltypes); else if ((pi.IsClass || pi.IsStruct) && v is Dictionary) oset = ParseDictionary((Dictionary)v, globaltypes, pi.pt, pi.getter(o)); else if (v is List) oset = CreateArray((List)v, pi.pt, typeof(object), globaltypes); else if (pi.IsValueType) oset = ChangeType(v, pi.changeType); else oset = v; } break; } o = pi.setter(o, oset); } } } return o; } private StringDictionary CreateSD(Dictionary d) { StringDictionary nv = new StringDictionary(); foreach (var o in d) nv.Add(o.Key, (string)o.Value); return nv; } private NameValueCollection CreateNV(Dictionary d) { NameValueCollection nv = new NameValueCollection(); foreach (var o in d) nv.Add(o.Key, (string)o.Value); return nv; } private void ProcessMap(object obj, Dictionary props, Dictionary dic) { foreach (KeyValuePair kv in dic) { myPropInfo p = props[kv.Key]; object o = p.getter(obj); Type t = Type.GetType((string)kv.Value); if (t == typeof(Guid)) p.setter(obj, CreateGuid((string)o)); } } private int CreateInteger(string s, int index, int count) { int num = 0; bool neg = false; for (int x = 0; x < count; x++, index++) { char cc = s[index]; if (cc == '-') neg = true; else if (cc == '+') neg = false; else { num *= 10; num += (int)(cc - '0'); } } if (neg) num = -num; return num; } private object CreateEnum(Type pt, object v) { // TODO : optimize create enum #if !SILVERLIGHT return Enum.Parse(pt, v.ToString()); #else return Enum.Parse(pt, v, true); #endif } private Guid CreateGuid(string s) { if (s.Length > 30) return new Guid(s); else return new Guid(Convert.FromBase64String(s)); } private DateTime CreateDateTime(string value) { bool utc = false; // 0123456789012345678 9012 9/3 // datetime format = yyyy-MM-ddTHH:mm:ss .nnn Z int year; int month; int day; int hour; int min; int sec; int ms = 0; year = CreateInteger(value, 0, 4); month = CreateInteger(value, 5, 2); day = CreateInteger(value, 8, 2); hour = CreateInteger(value, 11, 2); min = CreateInteger(value, 14, 2); sec = CreateInteger(value, 17, 2); if (value.Length > 21 && value[19] == '.') ms = CreateInteger(value, 20, 3); if (value[value.Length - 1] == 'Z') utc = true; if (_params.UseUTCDateTime == false && utc == false) return new DateTime(year, month, day, hour, min, sec, ms); else return new DateTime(year, month, day, hour, min, sec, ms, DateTimeKind.Utc).ToLocalTime(); } private object CreateArray(List data, Type pt, Type bt, Dictionary globalTypes) { Array col = Array.CreateInstance(bt, data.Count); // create an array of objects for (int i = 0; i < data.Count; i++) { object ob = data[i]; if (ob == null) { continue; } if (ob is IDictionary) col.SetValue(ParseDictionary((Dictionary)ob, globalTypes, bt, null), i); else if (ob is ICollection) col.SetValue(CreateArray((List)ob, bt, bt.GetElementType(), globalTypes), i); else col.SetValue(ChangeType(ob, bt), i); } return col; } private object CreateGenericList(List data, Type pt, Type bt, Dictionary globalTypes) { IList col = (IList)Reflection.Instance.FastCreateInstance(pt); // create an array of objects foreach (object ob in data) { if (ob is IDictionary) col.Add(ParseDictionary((Dictionary)ob, globalTypes, bt, null)); else if (ob is List) { if (bt.IsGenericType) col.Add((List)ob);//).ToArray()); else col.Add(((List)ob).ToArray()); } else col.Add(ChangeType(ob, bt)); } return col; } private object CreateStringKeyDictionary(Dictionary reader, Type pt, Type[] types, Dictionary globalTypes) { var col = (IDictionary)Reflection.Instance.FastCreateInstance(pt); Type t1 = null; Type t2 = null; if (types != null) { t1 = types[0]; t2 = types[1]; } foreach (KeyValuePair values in reader) { var key = values.Key; object val = null; if (values.Value is Dictionary) val = ParseDictionary((Dictionary)values.Value, globalTypes, t2, null); else if (types != null && t2.IsArray) { if (values.Value is Array) val = values.Value; else val = CreateArray((List)values.Value, t2, t2.GetElementType(), globalTypes); } else if (values.Value is IList) val = CreateGenericList((List)values.Value, t2, t1, globalTypes); else val = ChangeType(values.Value, t2); col.Add(key, val); } return col; } private object CreateDictionary(List reader, Type pt, Type[] types, Dictionary globalTypes) { IDictionary col = (IDictionary)Reflection.Instance.FastCreateInstance(pt); Type t1 = null; Type t2 = null; if (types != null) { t1 = types[0]; t2 = types[1]; } foreach (Dictionary values in reader) { object key = values["k"]; object val = values["v"]; if (key is Dictionary) key = ParseDictionary((Dictionary)key, globalTypes, t1, null); else key = ChangeType(key, t1); if (val is Dictionary) val = ParseDictionary((Dictionary)val, globalTypes, t2, null); else val = ChangeType(val, t2); col.Add(key, val); } return col; } #if !SILVERLIGHT private DataSet CreateDataset(Dictionary reader, Dictionary globalTypes) { DataSet ds = new DataSet(); ds.EnforceConstraints = false; ds.BeginInit(); // read dataset schema here var schema = reader["$schema"]; if (schema is string) { TextReader tr = new StringReader((string)schema); ds.ReadXmlSchema(tr); } else { DatasetSchema ms = (DatasetSchema)ParseDictionary((Dictionary)schema, globalTypes, typeof(DatasetSchema), null); ds.DataSetName = ms.Name; for (int i = 0; i < ms.Info.Count; i += 3) { if (ds.Tables.Contains(ms.Info[i]) == false) ds.Tables.Add(ms.Info[i]); ds.Tables[ms.Info[i]].Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2])); } } foreach (KeyValuePair pair in reader) { if (pair.Key == "$type" || pair.Key == "$schema") continue; List rows = (List)pair.Value; if (rows == null) continue; DataTable dt = ds.Tables[pair.Key]; ReadDataTable(rows, dt); } ds.EndInit(); return ds; } private void ReadDataTable(List rows, DataTable dt) { dt.BeginInit(); dt.BeginLoadData(); List guidcols = new List(); List datecol = new List(); foreach (DataColumn c in dt.Columns) { if (c.DataType == typeof(Guid) || c.DataType == typeof(Guid?)) guidcols.Add(c.Ordinal); if (_params.UseUTCDateTime && (c.DataType == typeof(DateTime) || c.DataType == typeof(DateTime?))) datecol.Add(c.Ordinal); } foreach (List row in rows) { object[] v = new object[row.Count]; row.CopyTo(v, 0); foreach (int i in guidcols) { string s = (string)v[i]; if (s != null && s.Length < 36) v[i] = new Guid(Convert.FromBase64String(s)); } if (_params.UseUTCDateTime) { foreach (int i in datecol) { string s = (string)v[i]; if (s != null) v[i] = CreateDateTime(s); } } dt.Rows.Add(v); } dt.EndLoadData(); dt.EndInit(); } DataTable CreateDataTable(Dictionary reader, Dictionary globalTypes) { var dt = new DataTable(); // read dataset schema here var schema = reader["$schema"]; if (schema is string) { TextReader tr = new StringReader((string)schema); dt.ReadXmlSchema(tr); } else { var ms = (DatasetSchema)this.ParseDictionary((Dictionary)schema, globalTypes, typeof(DatasetSchema), null); dt.TableName = ms.Info[0]; for (int i = 0; i < ms.Info.Count; i += 3) { dt.Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2])); } } foreach (var pair in reader) { if (pair.Key == "$type" || pair.Key == "$schema") continue; var rows = (List)pair.Value; if (rows == null) continue; if (!dt.TableName.Equals(pair.Key, StringComparison.InvariantCultureIgnoreCase)) continue; ReadDataTable(rows, dt); } return dt; } #endif #endregion } }