using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Globalization; using System.IO; using System.Text; internal class JSONSerializer { private readonly StringBuilder _output = new StringBuilder(); private readonly int _MAX_DEPTH = 10; private int _current_depth = 0; private Dictionary _globalTypes = new Dictionary(); private JSONParameters _params; private bool _TypesWritten = false; internal JSONSerializer(JSONParameters param) { _params = param; } internal string ConvertToJSON(object obj) { WriteValue(obj); string str = ""; if (_params.UsingGlobalTypes) { StringBuilder sb = new StringBuilder(); sb.Append("\"$types\":{"); bool pendingSeparator = false; foreach (KeyValuePair kv in _globalTypes) { if (pendingSeparator) { sb.Append(','); } pendingSeparator = true; sb.Append("\""); sb.Append(kv.Key); sb.Append("\":\""); sb.Append(kv.Value); sb.Append("\""); } sb.Append("},"); return _output.Replace("$types$", sb.ToString()).ToString(); } return _output.ToString(); } private void WriteValue(object obj) { if (obj == null || obj is DBNull) { _output.Append("null"); } else if (obj is string || obj is char) { WriteString(obj.ToString()); } else if (obj is Guid) { WriteGuid((Guid)obj); } else if (obj is bool) { _output.Append(((bool)obj) ? "true" : "false"); } else if (obj is int || obj is long || obj is double || obj is decimal || obj is float || obj is byte || obj is short || obj is sbyte || obj is ushort || obj is uint || obj is ulong) { _output.Append(((IConvertible)obj).ToString(NumberFormatInfo.InvariantInfo)); } else if (obj is DateTime) { WriteDateTime((DateTime)obj); } else if (obj is IDictionary && obj.GetType().IsGenericType && obj.GetType().GetGenericArguments()[0] == typeof(string)) { WriteStringDictionary((IDictionary)obj); } else if (obj is IDictionary) { WriteDictionary((IDictionary)obj); } else if (obj is DataSet) { WriteDataset((DataSet)obj); } else if (obj is DataTable) { WriteDataTable((DataTable)obj); } else if (obj is byte[]) { WriteBytes((byte[])obj); } else if (obj is Array || obj is IList || obj is ICollection) { WriteArray((IEnumerable)obj); } else if (obj is Enum) { WriteEnum((Enum)obj); } else { WriteObject(obj); } } private void WriteEnum(Enum e) { WriteStringFast(e.ToString()); } private void WriteGuid(Guid g) { if (!_params.UseFastGuid) { WriteStringFast(g.ToString()); } else { WriteBytes(g.ToByteArray()); } } private void WriteBytes(byte[] bytes) { WriteStringFast(Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None)); } private void WriteDateTime(DateTime dateTime) { DateTime dt = dateTime; if (_params.UseUTCDateTime) { dt = dateTime.ToUniversalTime(); } _output.Append("\""); _output.Append(dt.Year.ToString("0000", NumberFormatInfo.InvariantInfo)); _output.Append("-"); _output.Append(dt.Month.ToString("00", NumberFormatInfo.InvariantInfo)); _output.Append("-"); _output.Append(dt.Day.ToString("00", NumberFormatInfo.InvariantInfo)); _output.Append(" "); _output.Append(dt.Hour.ToString("00", NumberFormatInfo.InvariantInfo)); _output.Append(":"); _output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo)); _output.Append(":"); _output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo)); if (_params.UseUTCDateTime) { _output.Append("Z"); } _output.Append("\""); } private DatasetSchema GetSchema(DataTable ds) { if (ds == null) { return null; } DatasetSchema i = new DatasetSchema(); i.Info = new List(); i.Name = ds.TableName; foreach (DataColumn c in ds.Columns) { i.Info.Add(ds.TableName); i.Info.Add(c.ColumnName); i.Info.Add(c.DataType.ToString()); } return i; } private DatasetSchema GetSchema(DataSet ds) { if (ds == null) { return null; } DatasetSchema i = new DatasetSchema(); i.Info = new List(); i.Name = ds.DataSetName; foreach (DataTable t in ds.Tables) { foreach (DataColumn c in t.Columns) { i.Info.Add(t.TableName); i.Info.Add(c.ColumnName); i.Info.Add(c.DataType.ToString()); } } return i; } private string GetXmlSchema(DataTable dt) { using StringWriter writer = new StringWriter(); dt.WriteXmlSchema(writer); return dt.ToString(); } private void WriteDataset(DataSet ds) { _output.Append('{'); if (_params.UseExtensions) { WritePair("$schema", _params.UseOptimizedDatasetSchema ? ((object)GetSchema(ds)) : ((object)ds.GetXmlSchema())); _output.Append(','); } bool tablesep = false; foreach (DataTable table in ds.Tables) { if (tablesep) { _output.Append(","); } tablesep = true; WriteDataTableData(table); } _output.Append('}'); } private void WriteDataTableData(DataTable table) { _output.Append('"'); _output.Append(table.TableName); _output.Append("\":["); DataColumnCollection cols = table.Columns; bool rowseparator = false; foreach (DataRow row in table.Rows) { if (rowseparator) { _output.Append(","); } rowseparator = true; _output.Append('['); bool pendingSeperator = false; foreach (DataColumn column in cols) { if (pendingSeperator) { _output.Append(','); } WriteValue(row[column]); pendingSeperator = true; } _output.Append(']'); } _output.Append(']'); } private void WriteDataTable(DataTable dt) { _output.Append('{'); if (_params.UseExtensions) { WritePair("$schema", _params.UseOptimizedDatasetSchema ? ((object)GetSchema(dt)) : ((object)GetXmlSchema(dt))); _output.Append(','); } WriteDataTableData(dt); _output.Append('}'); } private void WriteObject(object obj) { if (!_params.UsingGlobalTypes) { _output.Append('{'); } else if (!_TypesWritten) { _output.Append("{$types$"); } else { _output.Append("{"); } _TypesWritten = true; _current_depth++; if (_current_depth > _MAX_DEPTH) { throw new Exception("Serializer encountered maximum depth of " + _MAX_DEPTH); } Dictionary map = new Dictionary(); Type t = obj.GetType(); bool append = false; if (_params.UseExtensions) { if (!_params.UsingGlobalTypes) { WritePairFast("$type", Reflection.Instance.GetTypeAssemblyName(t)); } else { int dt = 0; string ct = Reflection.Instance.GetTypeAssemblyName(t); if (!_globalTypes.TryGetValue(ct, out dt)) { dt = _globalTypes.Count + 1; _globalTypes.Add(ct, dt); } WritePairFast("$type", dt.ToString()); } append = true; } List g = Reflection.Instance.GetGetters(t); foreach (Getters p in g) { if (append) { _output.Append(','); } object o = p.Getter(obj); if ((o == null || o is DBNull) && !_params.SerializeNullValues) { append = false; continue; } WritePair(p.Name, o); if (o != null && _params.UseExtensions) { Type tt = o.GetType(); if (tt == typeof(object)) { map.Add(p.Name, tt.ToString()); } } append = true; } if (map.Count > 0 && _params.UseExtensions) { _output.Append(",\"$map\":"); WriteStringDictionary(map); } _current_depth--; _output.Append('}'); _current_depth--; } private void WritePairFast(string name, string value) { if (value != null || _params.SerializeNullValues) { WriteStringFast(name); _output.Append(':'); WriteStringFast(value); } } private void WritePair(string name, object value) { if ((value != null && !(value is DBNull)) || _params.SerializeNullValues) { WriteStringFast(name); _output.Append(':'); WriteValue(value); } } private void WriteArray(IEnumerable array) { _output.Append('['); bool pendingSeperator = false; foreach (object obj in array) { if (pendingSeperator) { _output.Append(','); } WriteValue(obj); pendingSeperator = true; } _output.Append(']'); } private void WriteStringDictionary(IDictionary dic) { _output.Append('{'); bool pendingSeparator = false; foreach (DictionaryEntry entry in dic) { if (pendingSeparator) { _output.Append(','); } WritePair((string)entry.Key, entry.Value); pendingSeparator = true; } _output.Append('}'); } private void WriteDictionary(IDictionary dic) { _output.Append('['); bool pendingSeparator = false; foreach (DictionaryEntry entry in dic) { if (pendingSeparator) { _output.Append(','); } _output.Append('{'); WritePair("k", entry.Key); _output.Append(","); WritePair("v", entry.Value); _output.Append('}'); pendingSeparator = true; } _output.Append(']'); } private void WriteStringFast(string s) { _output.Append('"'); _output.Append(s); _output.Append('"'); } private void WriteString(string s) { _output.Append('"'); int runIndex = -1; for (int index = 0; index < s.Length; index++) { char c = s[index]; if (c >= ' ' && c < '\u0080' && c != '"' && c != '\\') { if (runIndex == -1) { runIndex = index; } continue; } if (runIndex != -1) { _output.Append(s, runIndex, index - runIndex); runIndex = -1; } switch (c) { case '\t': _output.Append("\\t"); break; case '\r': _output.Append("\\r"); break; case '\n': _output.Append("\\n"); break; case '"': case '\\': _output.Append('\\'); _output.Append(c); break; default: { _output.Append("\\u"); StringBuilder output = _output; int num = c; output.Append(num.ToString("X4", NumberFormatInfo.InvariantInfo)); break; } } } if (runIndex != -1) { _output.Append(s, runIndex, s.Length - runIndex); } _output.Append('"'); } }