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; #if !__MonoCS__ #pragma warning disable CS1591 #endif 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; /// /// ** work in progress /// 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 = true; public void FixValues() { if (UseExtensions == false) // disable conflicting params { UsingGlobalTypes = false; } } } public sealed class JSON { //public readonly static JSON Instance = new JSON(); [ThreadStatic] private static JSON _instance; public static JSON Instance { get { return _instance ?? (_instance = new JSON()); } } private JSON() { } /// /// You can set these paramters globally for all calls /// public JSONParameters Parameters = new JSONParameters(); private JSONParameters _params; public string ToJSON(object obj) { _params = Parameters; _params.FixValues(); Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; return ToJSON(obj, Parameters); } public string ToJSON(object obj, JSONParameters param) { _params = param; _params.FixValues(); Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; Type t = null; if (obj == null) return "null"; if (obj.GetType().IsGenericType) t = obj.GetType().GetGenericTypeDefinition(); if (t == typeof(Dictionary<,>) || t == typeof(List<>)) _params.UsingGlobalTypes = false; // FEATURE : enable extensions when you can deserialize anon types if (_params.EnableAnonymousTypes) { _params.UseExtensions = false; _params.UsingGlobalTypes = false; Reflection.Instance.ShowReadOnlyProperties = true; } _usingglobals = _params.UsingGlobalTypes; return new JSONSerializer(_params).ConvertToJSON(obj); } public object Parse(string json) { _params = Parameters; Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; return new JsonParser(json, _params.IgnoreCaseOnDeserialize).Decode(); } public T ToObject(string json) { return (T)ToObject(json, typeof(T)); } public object ToObject(string json) { return ToObject(json, null); } public object ToObject(string json, Type type) { _params = Parameters; _params.FixValues(); Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; Type t = null; if (type != null && type.IsGenericType) t = type.GetGenericTypeDefinition(); if (t == typeof(Dictionary<,>) || t == typeof(List<>)) _params.UsingGlobalTypes = false; _usingglobals = _params.UsingGlobalTypes; object o = new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode(); if (o == null) return null; #if !SILVERLIGHT if (type != null && type == typeof(DataSet)) return CreateDataset(o as Dictionary, null); if (type != null && type == typeof(DataTable)) return CreateDataTable(o as Dictionary, null); #endif if (o is IDictionary) { if (type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) // deserialize a dictionary return RootDictionary(o, type); else // deserialize an object return ParseDictionary(o as Dictionary, null, type, null); } if (o is List) { if (type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) // kv format return RootDictionary(o, type); if (type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) // deserialize to generic list return RootList(o, type); else return (o as List).ToArray(); } if (type != null && o.GetType() != type) return ChangeType(o, type); return o; } public string Beautify(string input) { return Formatter.PrettyPrint(input); } public object FillObject(object input, string json) { _params = Parameters; _params.FixValues(); Reflection.Instance.ShowReadOnlyProperties = _params.ShowReadOnlyProperties; Dictionary ht = new JsonParser(json, Parameters.IgnoreCaseOnDeserialize).Decode() as Dictionary; if (ht == null) return null; return ParseDictionary(ht, null, input.GetType(), input); } public object DeepCopy(object obj) { return ToObject(ToJSON(obj)); } public T DeepCopy(T obj) { return ToObject(ToJSON(obj)); } internal SafeDictionary _customSerializer = new SafeDictionary(); internal SafeDictionary _customDeserializer = new SafeDictionary(); public void RegisterCustomType(Type type, Serialize serializer, Deserialize deserializer) { if (type != null && serializer != null && deserializer != null) { _customSerializer.Add(type, serializer); _customDeserializer.Add(type, deserializer); // reset property cache _propertycache = new SafeDictionary>(); } } internal bool IsTypeRegistered(Type t) { if (_customSerializer.Count == 0) return false; Serialize s; return _customSerializer.TryGetValue(t, out s); } #region [ JSON specific reflection ] private enum myPropInfoType { Int, Long, String, Bool, DateTime, Enum, Guid, Array, ByteArray, Dictionary, StringDictionary, #if !SILVERLIGHT Hashtable, DataSet, DataTable, #endif Custom, Unknown, } [Flags] private enum myPropInfoFlags { Filled = 1 << 0, CanWrite = 1 << 1 } private struct myPropInfo { public Type pt; public Type bt; public Type changeType; public Reflection.GenericSetter setter; public Reflection.GenericGetter getter; public Type[] GenericTypes; public string Name; public myPropInfoType Type; public myPropInfoFlags Flags; public bool IsClass; public bool IsValueType; public bool IsGenericType; } SafeDictionary> _propertycache = new SafeDictionary>(); private SafeDictionary Getproperties(Type type, string typename) { SafeDictionary sd = null; if (_propertycache.TryGetValue(typename, out sd)) { return sd; } else { sd = new SafeDictionary(); PropertyInfo[] pr = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo p in pr) { myPropInfo d = CreateMyProp(p.PropertyType, p.Name); d.Flags |= myPropInfoFlags.CanWrite; d.setter = Reflection.CreateSetMethod(type, p); d.getter = Reflection.CreateGetMethod(type, p); sd.Add(p.Name, d); } FieldInfo[] fi = type.GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo f in fi) { myPropInfo d = CreateMyProp(f.FieldType, f.Name); d.setter = Reflection.CreateSetField(type, f); d.getter = Reflection.CreateGetField(type, f); sd.Add(f.Name, d); } _propertycache.Add(typename, sd); return sd; } } private myPropInfo CreateMyProp(Type t, string name) { myPropInfo d = new myPropInfo(); myPropInfoType d_type = myPropInfoType.Unknown; myPropInfoFlags d_flags = myPropInfoFlags.Filled | myPropInfoFlags.CanWrite; if (t == typeof(int) || t == typeof(int?)) d_type = myPropInfoType.Int; else if (t == typeof(long) || t == typeof(long?)) d_type = myPropInfoType.Long; else if (t == typeof(string)) d_type = myPropInfoType.String; else if (t == typeof(bool) || t == typeof(bool?)) d_type = myPropInfoType.Bool; else if (t == typeof(DateTime) || t == typeof(DateTime?)) d_type = myPropInfoType.DateTime; else if (t.IsEnum) d_type = myPropInfoType.Enum; else if (t == typeof(Guid) || t == typeof(Guid?)) d_type = myPropInfoType.Guid; else if (t.IsArray) { d.bt = t.GetElementType(); if (t == typeof(byte[])) d_type = myPropInfoType.ByteArray; else d_type = myPropInfoType.Array; } else if (t.Name.Contains("Dictionary")) { d.GenericTypes = t.GetGenericArguments(); if (d.GenericTypes.Length > 0 && d.GenericTypes[0] == typeof(string)) d_type = myPropInfoType.StringDictionary; else d_type = myPropInfoType.Dictionary; } #if !SILVERLIGHT else if (t == typeof(Hashtable)) d_type = myPropInfoType.Hashtable; else if (t == typeof(DataSet)) d_type = myPropInfoType.DataSet; else if (t == typeof(DataTable)) d_type = myPropInfoType.DataTable; #endif else if (IsTypeRegistered(t)) d_type = myPropInfoType.Custom; d.IsClass = t.IsClass; d.IsValueType = t.IsValueType; if (t.IsGenericType) { d.IsGenericType = true; d.bt = t.GetGenericArguments()[0]; } d.pt = t; d.Name = name; d.changeType = GetChangeType(t); d.Type = d_type; d.Flags = d_flags; return d; } 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 == typeof(Guid)) return CreateGuid((string)value); else if (conversionType.IsEnum) return CreateEnum(conversionType, (string)value); else if (IsTypeRegistered(conversionType)) return CreateCustom((string)value, conversionType); return Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture); } #endregion #region [ p r i v a t e m e t h o d s ] private object RootList(object parse, Type type) { Type[] gtypes = type.GetGenericArguments(); 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 = type.GetGenericArguments(); if (parse is Dictionary) { IDictionary o = (IDictionary)Reflection.Instance.FastCreateInstance(type); foreach (var kv in (Dictionary)parse) { object v; object k = ChangeType(kv.Key, gtypes[0]); if (kv.Value is Dictionary) v = ParseDictionary(kv.Value as Dictionary, null, gtypes[1], null); else if (kv.Value is List) v = CreateArray(kv.Value as List, typeof(object), typeof(object), null); else v = ChangeType(kv.Value, gtypes[1]); o.Add(k, v); } return o; } if (parse is List) return CreateDictionary(parse as List, type, gtypes, null); return null; } bool _usingglobals = false; private object ParseDictionary(Dictionary d, Dictionary globaltypes, Type type, object input) { object tn = ""; 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 CreateDataset(d, globaltypes); } #endif if (found) { if (_usingglobals) { object tname = ""; if (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) o = Reflection.Instance.FastCreateInstance(type); SafeDictionary props = Getproperties(type, typename); foreach (string n in d.Keys) { string name = n; if (_params.IgnoreCaseOnDeserialize) name = name.ToLower(); if (name == "$map") { ProcessMap(o, props, (Dictionary)d[name]); continue; } myPropInfo pi; if (props.TryGetValue(name, out pi) == false) continue; if ((pi.Flags & (myPropInfoFlags.Filled | myPropInfoFlags.CanWrite)) != 0) { object v = d[name]; 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, (string)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 = this.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.StringDictionary: oset = CreateStringKeyDictionary((Dictionary)v, pi.pt, pi.GenericTypes, globaltypes); break; case myPropInfoType.Custom: oset = 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 && 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 object CreateCustom(string v, Type type) { Deserialize d; _customDeserializer.TryGetValue(type, out d); return d(v); } private void ProcessMap(object obj, SafeDictionary props, Dictionary dic) { foreach (KeyValuePair kv in dic) { myPropInfo p = props[kv.Key]; object o = p.getter(obj); Type t = Reflection.Instance.GetTypeFromCache((string)kv.Value); if (t == typeof(Guid)) p.setter(obj, CreateGuid((string)o)); } } static int CreateInteger(out int 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 static long CreateLong(out long num, char[] 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; } private object CreateEnum(Type pt, string v) { // TODO : optimize create enum #if !SILVERLIGHT return Enum.Parse(pt, v); #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 // datetime format = yyyy-MM-dd HH:mm:ss int year;// = (int)CreateLong(value.Substring(0, 4)); int month;// = (int)CreateLong(value.Substring(5, 2)); int day;// = (int)CreateLong(value.Substring(8, 2)); int hour;// = (int)CreateLong(value.Substring(11, 2)); int min;// = (int)CreateLong(value.Substring(14, 2)); int sec;// = (int)CreateLong(value.Substring(17, 2)); CreateInteger(out year, value, 0, 4); CreateInteger(out month, value, 5, 2); CreateInteger(out day, value, 8, 2); CreateInteger(out hour, value, 11, 2); CreateInteger(out min, value, 14, 2); CreateInteger(out sec, value, 17, 2); //if (value.EndsWith("Z")) if (value[value.Length - 1] == 'Z') utc = true; if (_params.UseUTCDateTime == false && utc == false) return new DateTime(year, month, day, hour, min, sec); else return new DateTime(year, month, day, hour, min, sec, 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++)// each (object ob in data) { object ob = data[i]; if (ob is IDictionary) //col.SetValue(ParseDictionary((Dictionary)ob, globalTypes, bt, null), i); // Original line col.SetValue(RootDictionary(ob, typeof(Dictionary)), i); // Change for UE4 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) 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;//ChangeType(values.Key, t1); object val = null; if (values.Value is Dictionary) val = ParseDictionary((Dictionary)values.Value, globalTypes, t2, null); 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; } private Type GetChangeType(Type conversionType) { if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) return conversionType.GetGenericArguments()[0]; return conversionType; } #if !SILVERLIGHT private DataSet CreateDataset(Dictionary reader, Dictionary globalTypes) { DataSet ds = new DataSet(); ds.EnforceConstraints = false; ds.BeginInit(); // read dataset schema here ReadSchema(reader, ds, globalTypes); 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 ReadSchema(Dictionary reader, DataSet ds, Dictionary globalTypes) { 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], Reflection.Instance.GetTypeFromCache(ms.Info[i + 2])); } } } 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], Reflection.Instance.GetTypeFromCache(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 } } #if !__MonoCS__ #pragma warning restore CS1591 #endif