JsonSerializer.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. #if !SILVERLIGHT
  5. using System.Data;
  6. #endif
  7. using System.Globalization;
  8. using System.IO;
  9. using System.Text;
  10. using System.Collections.Specialized;
  11. namespace fastJSON
  12. {
  13. internal sealed class JSONSerializer
  14. {
  15. private StringBuilder _output = new StringBuilder();
  16. //private StringBuilder _before = new StringBuilder();
  17. private int _before;
  18. private int _MAX_DEPTH = 20;
  19. int _current_depth = 0;
  20. private Dictionary<string, int> _globalTypes = new Dictionary<string, int>();
  21. private Dictionary<object, int> _cirobj = new Dictionary<object, int>();
  22. private JSONParameters _params;
  23. private bool _useEscapedUnicode = false;
  24. internal JSONSerializer(JSONParameters param)
  25. {
  26. _params = param;
  27. _useEscapedUnicode = _params.UseEscapedUnicode;
  28. _MAX_DEPTH = _params.SerializerMaxDepth;
  29. }
  30. internal string ConvertToJSON(object obj)
  31. {
  32. WriteValue(obj);
  33. if (_params.UsingGlobalTypes && _globalTypes != null && _globalTypes.Count > 0)
  34. {
  35. var sb = new StringBuilder();
  36. sb.Append("\"$types\":{");
  37. var pendingSeparator = false;
  38. foreach (var kv in _globalTypes)
  39. {
  40. if (pendingSeparator) sb.Append(',');
  41. pendingSeparator = true;
  42. sb.Append('\"');
  43. sb.Append(kv.Key);
  44. sb.Append("\":\"");
  45. sb.Append(kv.Value);
  46. sb.Append('\"');
  47. }
  48. sb.Append("},");
  49. _output.Insert(_before, sb.ToString());
  50. }
  51. return _output.ToString();
  52. }
  53. private void WriteValue(object obj)
  54. {
  55. if (obj == null || obj is DBNull)
  56. _output.Append("null");
  57. else if (obj is string || obj is char)
  58. WriteString(obj.ToString());
  59. else if (obj is Guid)
  60. WriteGuid((Guid)obj);
  61. else if (obj is bool)
  62. _output.Append(((bool)obj) ? "true" : "false"); // conform to standard
  63. else if (
  64. obj is int || obj is long ||
  65. obj is decimal ||
  66. obj is byte || obj is short ||
  67. obj is sbyte || obj is ushort ||
  68. obj is uint || obj is ulong
  69. )
  70. _output.Append(((IConvertible)obj).ToString(NumberFormatInfo.InvariantInfo));
  71. else if (obj is double || obj is Double)
  72. {
  73. double d = (double)obj;
  74. if (double.IsNaN(d))
  75. _output.Append("\"NaN\"");
  76. else
  77. _output.Append(((IConvertible)obj).ToString(NumberFormatInfo.InvariantInfo));
  78. }
  79. else if (obj is float || obj is Single)
  80. {
  81. float d = (float)obj;
  82. if (float.IsNaN(d))
  83. _output.Append("\"NaN\"");
  84. else
  85. _output.Append(((IConvertible)obj).ToString(NumberFormatInfo.InvariantInfo));
  86. }
  87. else if (obj is DateTime)
  88. WriteDateTime((DateTime)obj);
  89. else if (obj is DateTimeOffset)
  90. WriteDateTimeOffset((DateTimeOffset)obj);
  91. else if (obj is TimeSpan)
  92. _output.Append(((TimeSpan)obj).Ticks);
  93. #if net4
  94. else if (_params.KVStyleStringDictionary == false &&
  95. obj is IEnumerable<KeyValuePair<string, object>>)
  96. WriteStringDictionary((IEnumerable<KeyValuePair<string, object>>)obj);
  97. #endif
  98. else if (_params.KVStyleStringDictionary == false && obj is IDictionary &&
  99. obj.GetType().IsGenericType && obj.GetType().GetGenericArguments()[0] == typeof(string))
  100. WriteStringDictionary((IDictionary)obj);
  101. else if (obj is IDictionary)
  102. WriteDictionary((IDictionary)obj);
  103. #if !SILVERLIGHT
  104. else if (obj is DataSet)
  105. WriteDataset((DataSet)obj);
  106. else if (obj is DataTable)
  107. this.WriteDataTable((DataTable)obj);
  108. #endif
  109. else if (obj is byte[])
  110. WriteBytes((byte[])obj);
  111. else if (obj is StringDictionary)
  112. WriteSD((StringDictionary)obj);
  113. else if (obj is NameValueCollection)
  114. WriteNV((NameValueCollection)obj);
  115. else if (obj is IEnumerable)
  116. WriteArray((IEnumerable)obj);
  117. else if (obj is Enum)
  118. WriteEnum((Enum)obj);
  119. else if (Reflection.Instance.IsTypeRegistered(obj.GetType()))
  120. WriteCustom(obj);
  121. else
  122. WriteObject(obj);
  123. }
  124. private void WriteDateTimeOffset(DateTimeOffset d)
  125. {
  126. write_date_value(d.DateTime);
  127. _output.Append(" ");
  128. if (d.Offset.Hours > 0)
  129. _output.Append("+");
  130. else
  131. _output.Append("-");
  132. _output.Append(d.Offset.Hours.ToString("00", NumberFormatInfo.InvariantInfo));
  133. _output.Append(":");
  134. _output.Append(d.Offset.Minutes);
  135. _output.Append('\"');
  136. }
  137. private void WriteNV(NameValueCollection nameValueCollection)
  138. {
  139. _output.Append('{');
  140. bool pendingSeparator = false;
  141. foreach (string key in nameValueCollection)
  142. {
  143. if (_params.SerializeNullValues == false && (nameValueCollection[key] == null))
  144. {
  145. }
  146. else
  147. {
  148. if (pendingSeparator) _output.Append(',');
  149. if (_params.SerializeToLowerCaseNames)
  150. WritePair(key.ToLower(), nameValueCollection[key]);
  151. else
  152. WritePair(key, nameValueCollection[key]);
  153. pendingSeparator = true;
  154. }
  155. }
  156. _output.Append('}');
  157. }
  158. private void WriteSD(StringDictionary stringDictionary)
  159. {
  160. _output.Append('{');
  161. bool pendingSeparator = false;
  162. foreach (DictionaryEntry entry in stringDictionary)
  163. {
  164. if (_params.SerializeNullValues == false && (entry.Value == null))
  165. {
  166. }
  167. else
  168. {
  169. if (pendingSeparator) _output.Append(',');
  170. string k = (string)entry.Key;
  171. if (_params.SerializeToLowerCaseNames)
  172. WritePair(k.ToLower(), entry.Value);
  173. else
  174. WritePair(k, entry.Value);
  175. pendingSeparator = true;
  176. }
  177. }
  178. _output.Append('}');
  179. }
  180. private void WriteCustom(object obj)
  181. {
  182. Serialize s;
  183. Reflection.Instance._customSerializer.TryGetValue(obj.GetType(), out s);
  184. WriteStringFast(s(obj));
  185. }
  186. private void WriteEnum(Enum e)
  187. {
  188. // FEATURE : optimize enum write
  189. if (_params.UseValuesOfEnums)
  190. WriteValue(Convert.ToInt32(e));
  191. else
  192. WriteStringFast(e.ToString());
  193. }
  194. private void WriteGuid(Guid g)
  195. {
  196. if (_params.UseFastGuid == false)
  197. WriteStringFast(g.ToString());
  198. else
  199. WriteBytes(g.ToByteArray());
  200. }
  201. private void WriteBytes(byte[] bytes)
  202. {
  203. #if !SILVERLIGHT
  204. WriteStringFast(Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None));
  205. #else
  206. WriteStringFast(Convert.ToBase64String(bytes, 0, bytes.Length));
  207. #endif
  208. }
  209. private void WriteDateTime(DateTime dateTime)
  210. {
  211. // datetime format standard : yyyy-MM-dd HH:mm:ss
  212. DateTime dt = dateTime;
  213. if (_params.UseUTCDateTime)
  214. dt = dateTime.ToUniversalTime();
  215. write_date_value(dt);
  216. if (_params.UseUTCDateTime)
  217. _output.Append('Z');
  218. _output.Append('\"');
  219. }
  220. private void write_date_value(DateTime dt)
  221. {
  222. _output.Append('\"');
  223. _output.Append(dt.Year.ToString("0000", NumberFormatInfo.InvariantInfo));
  224. _output.Append('-');
  225. _output.Append(dt.Month.ToString("00", NumberFormatInfo.InvariantInfo));
  226. _output.Append('-');
  227. _output.Append(dt.Day.ToString("00", NumberFormatInfo.InvariantInfo));
  228. _output.Append('T'); // strict ISO date compliance
  229. _output.Append(dt.Hour.ToString("00", NumberFormatInfo.InvariantInfo));
  230. _output.Append(':');
  231. _output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo));
  232. _output.Append(':');
  233. _output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo));
  234. if (_params.DateTimeMilliseconds)
  235. {
  236. _output.Append('.');
  237. _output.Append(dt.Millisecond.ToString("000", NumberFormatInfo.InvariantInfo));
  238. }
  239. }
  240. #if !SILVERLIGHT
  241. private DatasetSchema GetSchema(DataTable ds)
  242. {
  243. if (ds == null) return null;
  244. DatasetSchema m = new DatasetSchema();
  245. m.Info = new List<string>();
  246. m.Name = ds.TableName;
  247. foreach (DataColumn c in ds.Columns)
  248. {
  249. m.Info.Add(ds.TableName);
  250. m.Info.Add(c.ColumnName);
  251. m.Info.Add(c.DataType.ToString());
  252. }
  253. // FEATURE : serialize relations and constraints here
  254. return m;
  255. }
  256. private DatasetSchema GetSchema(DataSet ds)
  257. {
  258. if (ds == null) return null;
  259. DatasetSchema m = new DatasetSchema();
  260. m.Info = new List<string>();
  261. m.Name = ds.DataSetName;
  262. foreach (DataTable t in ds.Tables)
  263. {
  264. foreach (DataColumn c in t.Columns)
  265. {
  266. m.Info.Add(t.TableName);
  267. m.Info.Add(c.ColumnName);
  268. m.Info.Add(c.DataType.ToString());
  269. }
  270. }
  271. // FEATURE : serialize relations and constraints here
  272. return m;
  273. }
  274. private string GetXmlSchema(DataTable dt)
  275. {
  276. using (var writer = new StringWriter())
  277. {
  278. dt.WriteXmlSchema(writer);
  279. return dt.ToString();
  280. }
  281. }
  282. private void WriteDataset(DataSet ds)
  283. {
  284. _output.Append('{');
  285. if (_params.UseExtensions)
  286. {
  287. WritePair("$schema", _params.UseOptimizedDatasetSchema ? (object)GetSchema(ds) : ds.GetXmlSchema());
  288. _output.Append(',');
  289. }
  290. bool tablesep = false;
  291. foreach (DataTable table in ds.Tables)
  292. {
  293. if (tablesep) _output.Append(',');
  294. tablesep = true;
  295. WriteDataTableData(table);
  296. }
  297. // end dataset
  298. _output.Append('}');
  299. }
  300. private void WriteDataTableData(DataTable table)
  301. {
  302. _output.Append('\"');
  303. _output.Append(table.TableName);
  304. _output.Append("\":[");
  305. DataColumnCollection cols = table.Columns;
  306. bool rowseparator = false;
  307. foreach (DataRow row in table.Rows)
  308. {
  309. if (rowseparator) _output.Append(',');
  310. rowseparator = true;
  311. _output.Append('[');
  312. bool pendingSeperator = false;
  313. foreach (DataColumn column in cols)
  314. {
  315. if (pendingSeperator) _output.Append(',');
  316. WriteValue(row[column]);
  317. pendingSeperator = true;
  318. }
  319. _output.Append(']');
  320. }
  321. _output.Append(']');
  322. }
  323. void WriteDataTable(DataTable dt)
  324. {
  325. this._output.Append('{');
  326. if (_params.UseExtensions)
  327. {
  328. this.WritePair("$schema", _params.UseOptimizedDatasetSchema ? (object)this.GetSchema(dt) : this.GetXmlSchema(dt));
  329. this._output.Append(',');
  330. }
  331. WriteDataTableData(dt);
  332. // end datatable
  333. this._output.Append('}');
  334. }
  335. #endif
  336. bool _TypesWritten = false;
  337. private void WriteObject(object obj)
  338. {
  339. int i = 0;
  340. if (_cirobj.TryGetValue(obj, out i) == false)
  341. _cirobj.Add(obj, _cirobj.Count + 1);
  342. else
  343. {
  344. if (_current_depth > 0 && _params.InlineCircularReferences == false)
  345. {
  346. //_circular = true;
  347. _output.Append("{\"$i\":");
  348. _output.Append(i.ToString());
  349. _output.Append("}");
  350. return;
  351. }
  352. }
  353. if (_params.UsingGlobalTypes == false)
  354. _output.Append('{');
  355. else
  356. {
  357. if (_TypesWritten == false)
  358. {
  359. _output.Append('{');
  360. _before = _output.Length;
  361. //_output = new StringBuilder();
  362. }
  363. else
  364. _output.Append('{');
  365. }
  366. _TypesWritten = true;
  367. _current_depth++;
  368. if (_current_depth > _MAX_DEPTH)
  369. throw new Exception("Serializer encountered maximum depth of " + _MAX_DEPTH);
  370. Dictionary<string, string> map = new Dictionary<string, string>();
  371. Type t = obj.GetType();
  372. bool append = false;
  373. if (_params.UseExtensions)
  374. {
  375. if (_params.UsingGlobalTypes == false)
  376. WritePairFast("$type", Reflection.Instance.GetTypeAssemblyName(t));
  377. else
  378. {
  379. int dt = 0;
  380. string ct = Reflection.Instance.GetTypeAssemblyName(t);
  381. if (_globalTypes.TryGetValue(ct, out dt) == false)
  382. {
  383. dt = _globalTypes.Count + 1;
  384. _globalTypes.Add(ct, dt);
  385. }
  386. WritePairFast("$type", dt.ToString());
  387. }
  388. append = true;
  389. }
  390. Getters[] g = Reflection.Instance.GetGetters(t, _params.ShowReadOnlyProperties, _params.IgnoreAttributes);
  391. int c = g.Length;
  392. for (int ii = 0; ii < c; ii++)
  393. {
  394. var p = g[ii];
  395. object o = p.Getter(obj);
  396. if (_params.SerializeNullValues == false && (o == null || o is DBNull))
  397. {
  398. //append = false;
  399. }
  400. else
  401. {
  402. if (append)
  403. _output.Append(',');
  404. if (_params.SerializeToLowerCaseNames)
  405. WritePair(p.lcName, o);
  406. else
  407. WritePair(p.Name, o);
  408. if (o != null && _params.UseExtensions)
  409. {
  410. Type tt = o.GetType();
  411. if (tt == typeof(System.Object))
  412. map.Add(p.Name, tt.ToString());
  413. }
  414. append = true;
  415. }
  416. }
  417. if (map.Count > 0 && _params.UseExtensions)
  418. {
  419. _output.Append(",\"$map\":");
  420. WriteStringDictionary(map);
  421. }
  422. _output.Append('}');
  423. _current_depth--;
  424. }
  425. private void WritePairFast(string name, string value)
  426. {
  427. WriteStringFast(name);
  428. _output.Append(':');
  429. WriteStringFast(value);
  430. }
  431. private void WritePair(string name, object value)
  432. {
  433. WriteString(name);
  434. _output.Append(':');
  435. WriteValue(value);
  436. }
  437. private void WriteArray(IEnumerable array)
  438. {
  439. _output.Append('[');
  440. bool pendingSeperator = false;
  441. foreach (object obj in array)
  442. {
  443. if (pendingSeperator) _output.Append(',');
  444. WriteValue(obj);
  445. pendingSeperator = true;
  446. }
  447. _output.Append(']');
  448. }
  449. private void WriteStringDictionary(IDictionary dic)
  450. {
  451. _output.Append('{');
  452. bool pendingSeparator = false;
  453. foreach (DictionaryEntry entry in dic)
  454. {
  455. if (_params.SerializeNullValues == false && (entry.Value == null))
  456. {
  457. }
  458. else
  459. {
  460. if (pendingSeparator) _output.Append(',');
  461. string k = (string)entry.Key;
  462. if (_params.SerializeToLowerCaseNames)
  463. WritePair(k.ToLower(), entry.Value);
  464. else
  465. WritePair(k, entry.Value);
  466. pendingSeparator = true;
  467. }
  468. }
  469. _output.Append('}');
  470. }
  471. private void WriteStringDictionary(IEnumerable<KeyValuePair<string, object>> dic)
  472. {
  473. _output.Append('{');
  474. bool pendingSeparator = false;
  475. foreach (KeyValuePair<string, object> entry in dic)
  476. {
  477. if (_params.SerializeNullValues == false && (entry.Value == null))
  478. {
  479. }
  480. else
  481. {
  482. if (pendingSeparator) _output.Append(',');
  483. string k = entry.Key;
  484. if (_params.SerializeToLowerCaseNames)
  485. WritePair(k.ToLower(), entry.Value);
  486. else
  487. WritePair(k, entry.Value);
  488. pendingSeparator = true;
  489. }
  490. }
  491. _output.Append('}');
  492. }
  493. private void WriteDictionary(IDictionary dic)
  494. {
  495. _output.Append('[');
  496. bool pendingSeparator = false;
  497. foreach (DictionaryEntry entry in dic)
  498. {
  499. if (pendingSeparator) _output.Append(',');
  500. _output.Append('{');
  501. WritePair("k", entry.Key);
  502. _output.Append(",");
  503. WritePair("v", entry.Value);
  504. _output.Append('}');
  505. pendingSeparator = true;
  506. }
  507. _output.Append(']');
  508. }
  509. private void WriteStringFast(string s)
  510. {
  511. _output.Append('\"');
  512. _output.Append(s);
  513. _output.Append('\"');
  514. }
  515. private void WriteString(string s)
  516. {
  517. _output.Append('\"');
  518. int runIndex = -1;
  519. int l = s.Length;
  520. for (var index = 0; index < l; ++index)
  521. {
  522. var c = s[index];
  523. if (_useEscapedUnicode)
  524. {
  525. if (c >= ' ' && c < 128 && c != '\"' && c != '\\')
  526. {
  527. if (runIndex == -1)
  528. runIndex = index;
  529. continue;
  530. }
  531. }
  532. else
  533. {
  534. if (c != '\t' && c != '\n' && c != '\r' && c != '\"' && c != '\\')// && c != ':' && c!=',')
  535. {
  536. if (runIndex == -1)
  537. runIndex = index;
  538. continue;
  539. }
  540. }
  541. if (runIndex != -1)
  542. {
  543. _output.Append(s, runIndex, index - runIndex);
  544. runIndex = -1;
  545. }
  546. switch (c)
  547. {
  548. case '\t': _output.Append("\\t"); break;
  549. case '\r': _output.Append("\\r"); break;
  550. case '\n': _output.Append("\\n"); break;
  551. case '"':
  552. case '\\': _output.Append('\\'); _output.Append(c); break;
  553. default:
  554. if (_useEscapedUnicode)
  555. {
  556. _output.Append("\\u");
  557. _output.Append(((int)c).ToString("X4", NumberFormatInfo.InvariantInfo));
  558. }
  559. else
  560. _output.Append(c);
  561. break;
  562. }
  563. }
  564. if (runIndex != -1)
  565. _output.Append(s, runIndex, s.Length - runIndex);
  566. _output.Append('\"');
  567. }
  568. }
  569. }