2
0

JsonSerializer.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. //
  2. // JsonSerializer.cs
  3. //
  4. // Author:
  5. // Marek Habersack <[email protected]>
  6. //
  7. // (C) 2008 Novell, Inc. http://novell.com/
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System;
  30. using System.Collections;
  31. using System.Collections.Generic;
  32. using System.Data;
  33. using System.Globalization;
  34. using System.Reflection;
  35. using System.Text;
  36. namespace System.Web.Script.Serialization
  37. {
  38. internal sealed class JsonSerializer
  39. {
  40. static readonly long InitialJavaScriptDateTicks = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
  41. static readonly DateTime MinimumJavaScriptDate = new DateTime (100, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  42. static readonly MethodInfo serializeGenericDictionary = typeof (JsonSerializer).GetMethod ("SerializeGenericDictionary", BindingFlags.NonPublic | BindingFlags.Static);
  43. Dictionary <object, bool> objectCache;
  44. JavaScriptSerializer serializer;
  45. JavaScriptTypeResolver typeResolver;
  46. int recursionLimit;
  47. int maxJsonLength;
  48. int recursionDepth;
  49. Dictionary <Type, MethodInfo> serializeGenericDictionaryMethods;
  50. public JsonSerializer (JavaScriptSerializer serializer)
  51. {
  52. if (serializer == null)
  53. throw new ArgumentNullException ("serializer");
  54. this.serializer = serializer;
  55. typeResolver = serializer.TypeResolver;
  56. recursionLimit = serializer.RecursionLimit;
  57. maxJsonLength = serializer.MaxJsonLength;
  58. }
  59. public void Serialize (object obj, StringBuilder output)
  60. {
  61. if (output == null)
  62. throw new ArgumentNullException ("output");
  63. recursionDepth = 0;
  64. objectCache = new Dictionary <object, bool> ();
  65. SerializeValue (obj, output);
  66. }
  67. void SerializeValue (object obj, StringBuilder output)
  68. {
  69. recursionDepth++;
  70. SerializeValueImpl (obj, output);
  71. recursionDepth--;
  72. }
  73. void SerializeValueImpl (object obj, StringBuilder output)
  74. {
  75. if (recursionDepth > recursionLimit)
  76. throw new ArgumentException ("Recursion limit has been exceeded while serializing object of type '{0}'", obj != null ? obj.GetType ().ToString () : "[null]");
  77. if (obj == null || DBNull.Value.Equals (obj)) {
  78. output.AppendCount (maxJsonLength, "null");
  79. return;
  80. }
  81. Type valueType = obj.GetType ();
  82. JavaScriptConverter jsc = serializer.GetConverter (valueType);
  83. if (jsc != null) {
  84. IDictionary <string, object> result = jsc.Serialize (obj, serializer);
  85. if (result == null) {
  86. output.AppendCount (maxJsonLength, "null");
  87. return;
  88. }
  89. if (typeResolver != null) {
  90. string typeId = typeResolver.ResolveTypeId (valueType);
  91. if (!String.IsNullOrEmpty (typeId))
  92. result [JavaScriptSerializer.SerializedTypeNameKey] = typeId;
  93. }
  94. SerializeValue (result, output);
  95. return;
  96. }
  97. TypeCode typeCode = Type.GetTypeCode (valueType);
  98. switch (typeCode) {
  99. case TypeCode.String:
  100. WriteValue (output, (string)obj);
  101. return;
  102. case TypeCode.Char:
  103. WriteValue (output, (char)obj);
  104. return;
  105. case TypeCode.Boolean:
  106. WriteValue (output, (bool)obj);
  107. return;
  108. case TypeCode.SByte:
  109. case TypeCode.Int16:
  110. case TypeCode.UInt16:
  111. case TypeCode.Int32:
  112. case TypeCode.Byte:
  113. case TypeCode.UInt32:
  114. case TypeCode.Int64:
  115. case TypeCode.UInt64:
  116. if (valueType.IsEnum) {
  117. WriteEnumValue (output, obj, typeCode);
  118. return;
  119. }
  120. goto case TypeCode.Single;
  121. case TypeCode.Single:
  122. case TypeCode.Double:
  123. case TypeCode.Decimal:
  124. WriteValue (output, obj as IConvertible);
  125. return;
  126. case TypeCode.DateTime:
  127. WriteValue (output, (DateTime)obj);
  128. return;
  129. }
  130. if (typeof (Uri).IsAssignableFrom (valueType)) {
  131. WriteValue (output, (Uri)obj);
  132. return;
  133. }
  134. if (typeof (Guid).IsAssignableFrom (valueType)) {
  135. WriteValue (output, (Guid)obj);
  136. return;
  137. }
  138. IConvertible convertible = obj as IConvertible;
  139. if (convertible != null) {
  140. WriteValue (output, convertible);
  141. return;
  142. }
  143. try {
  144. if (objectCache.ContainsKey (obj))
  145. throw new InvalidOperationException ("Circular reference detected.");
  146. objectCache.Add (obj, true);
  147. IDictionary dict = obj as IDictionary;
  148. if (dict != null) {
  149. SerializeDictionary (output, dict);
  150. return;
  151. }
  152. if (valueType.IsGenericType && typeof (IDictionary <,>).IsAssignableFrom (valueType.GetGenericTypeDefinition ())) {
  153. MethodInfo mi;
  154. if (!serializeGenericDictionaryMethods.TryGetValue (valueType, out mi)) {
  155. Type[] types = valueType.GetGenericArguments ();
  156. mi = serializeGenericDictionary.MakeGenericMethod (types [0], types [1]);
  157. serializeGenericDictionaryMethods.Add (valueType, mi);
  158. }
  159. mi.Invoke (this, new object[] {obj});
  160. return;
  161. }
  162. IEnumerable enumerable = obj as IEnumerable;
  163. if (enumerable != null) {
  164. SerializeEnumerable (output, enumerable);
  165. return;
  166. }
  167. SerializeArbitraryObject (output, obj, valueType);
  168. } finally {
  169. objectCache.Remove (obj);
  170. }
  171. }
  172. bool ShouldIgnoreMember (MemberInfo mi, out MethodInfo getMethod)
  173. {
  174. getMethod = null;
  175. if (mi.IsDefined (typeof (ScriptIgnoreAttribute), true))
  176. return true;
  177. FieldInfo fi = mi as FieldInfo;
  178. if (fi != null)
  179. return false;
  180. PropertyInfo pi = mi as PropertyInfo;
  181. if (pi == null)
  182. return true;
  183. getMethod = pi.GetGetMethod ();
  184. if (getMethod == null || getMethod.GetParameters ().Length > 0) {
  185. getMethod = null;
  186. return true;
  187. }
  188. return false;
  189. }
  190. object GetMemberValue (object obj, MemberInfo mi)
  191. {
  192. FieldInfo fi = mi as FieldInfo;
  193. if (fi != null)
  194. return fi.GetValue (obj);
  195. MethodInfo method = mi as MethodInfo;
  196. if (method == null)
  197. throw new InvalidOperationException ("Member is not a method (internal error).");
  198. object ret;
  199. try {
  200. ret = method.Invoke (obj, null);
  201. } catch (TargetInvocationException niex) {
  202. if (niex.InnerException is NotImplementedException) {
  203. Console.WriteLine ("!!! COMPATIBILITY WARNING. FEATURE NOT IMPLEMENTED. !!!");
  204. Console.WriteLine (niex);
  205. Console.WriteLine ("!!! RETURNING NULL. PLEASE LET MONO DEVELOPERS KNOW ABOUT THIS EXCEPTION. !!!");
  206. return null;
  207. }
  208. throw;
  209. }
  210. return ret;
  211. }
  212. void SerializeArbitraryObject (StringBuilder output, object obj, Type type)
  213. {
  214. output.AppendCount (maxJsonLength, "{");
  215. bool first = true;
  216. if (typeResolver != null) {
  217. string typeId = typeResolver.ResolveTypeId (type);
  218. if (!String.IsNullOrEmpty (typeId)) {
  219. WriteDictionaryEntry (output, first, JavaScriptSerializer.SerializedTypeNameKey, typeId);
  220. first = false;
  221. }
  222. }
  223. MemberInfo[] members = type.GetMembers (BindingFlags.Public | BindingFlags.Instance);
  224. MemberInfo member;
  225. MethodInfo getMethod;
  226. string name;
  227. foreach (MemberInfo mi in members) {
  228. if (ShouldIgnoreMember (mi, out getMethod))
  229. continue;
  230. name = mi.Name;
  231. if (getMethod != null)
  232. member = getMethod;
  233. else
  234. member = mi;
  235. WriteDictionaryEntry (output, first, name, GetMemberValue (obj, member));
  236. if (first)
  237. first = false;
  238. }
  239. output.AppendCount (maxJsonLength, "}");
  240. }
  241. void SerializeEnumerable (StringBuilder output, IEnumerable enumerable)
  242. {
  243. output.AppendCount (maxJsonLength, "[");
  244. bool first = true;
  245. foreach (object value in enumerable) {
  246. if (!first)
  247. output.AppendCount (maxJsonLength, ',');
  248. SerializeValue (value, output);
  249. if (first)
  250. first = false;
  251. }
  252. output.AppendCount (maxJsonLength, "]");
  253. }
  254. void SerializeDictionary (StringBuilder output, IDictionary dict)
  255. {
  256. output.AppendCount (maxJsonLength, "{");
  257. bool first = true;
  258. string key;
  259. foreach (DictionaryEntry entry in dict) {
  260. WriteDictionaryEntry (output, first, entry.Key as string, entry.Value);
  261. if (first)
  262. first = false;
  263. }
  264. output.AppendCount (maxJsonLength, "}");
  265. }
  266. void SerializeGenericDictionary <TKey, TValue> (StringBuilder output, IDictionary <TKey, TValue> dict)
  267. {
  268. output.AppendCount (maxJsonLength, "{");
  269. bool first = true;
  270. string key;
  271. foreach (KeyValuePair <TKey, TValue> kvp in dict) {
  272. WriteDictionaryEntry (output, first, kvp.Key as string, kvp.Value);
  273. if (first)
  274. first = false;
  275. }
  276. output.AppendCount (maxJsonLength, "}");
  277. }
  278. void WriteDictionaryEntry (StringBuilder output, bool skipComma, string key, object value)
  279. {
  280. if (key == null)
  281. throw new InvalidOperationException ("Only dictionaries with keys convertible to string are supported.");
  282. if (!skipComma)
  283. output.AppendCount (maxJsonLength, ',');
  284. WriteValue (output, key);
  285. output.AppendCount (maxJsonLength, ':');
  286. SerializeValue (value, output);
  287. }
  288. void WriteEnumValue (StringBuilder output, object value, TypeCode typeCode)
  289. {
  290. switch (typeCode) {
  291. case TypeCode.SByte:
  292. output.AppendCount (maxJsonLength, (sbyte)value);
  293. return;
  294. case TypeCode.Int16:
  295. output.AppendCount (maxJsonLength, (short)value);
  296. return;
  297. case TypeCode.UInt16:
  298. output.AppendCount (maxJsonLength, (ushort)value);
  299. return;
  300. case TypeCode.Int32:
  301. output.AppendCount (maxJsonLength, (int)value);
  302. return;
  303. case TypeCode.Byte:
  304. output.AppendCount (maxJsonLength, (byte)value);
  305. return;
  306. case TypeCode.UInt32:
  307. output.AppendCount (maxJsonLength, (uint)value);
  308. return;
  309. case TypeCode.Int64:
  310. output.AppendCount (maxJsonLength, (long)value);
  311. return;
  312. case TypeCode.UInt64:
  313. output.AppendCount (maxJsonLength, (ulong)value);
  314. return;
  315. default:
  316. throw new InvalidOperationException (String.Format ("Invalid type code for enum: {0}", typeCode));
  317. }
  318. }
  319. void WriteValue (StringBuilder output, Guid value)
  320. {
  321. WriteValue (output, value.ToString ());
  322. }
  323. void WriteValue (StringBuilder output, Uri value)
  324. {
  325. WriteValue (output, value.GetComponents (UriComponents.AbsoluteUri, UriFormat.UriEscaped));
  326. }
  327. void WriteValue (StringBuilder output, DateTime value)
  328. {
  329. value = value.ToUniversalTime ();
  330. if (value < MinimumJavaScriptDate)
  331. value = MinimumJavaScriptDate;
  332. long ticks = (value.Ticks - InitialJavaScriptDateTicks) / (long)10000;
  333. output.AppendCount (maxJsonLength, "\"\\/Date(" + ticks + ")\\/\"");
  334. }
  335. void WriteValue (StringBuilder output, IConvertible value)
  336. {
  337. output.AppendCount (maxJsonLength, value.ToString (CultureInfo.InvariantCulture));
  338. }
  339. void WriteValue (StringBuilder output, bool value)
  340. {
  341. output.AppendCount (maxJsonLength, value ? "true" : "false");
  342. }
  343. void WriteValue (StringBuilder output, char value)
  344. {
  345. if (value == '\0') {
  346. output.AppendCount (maxJsonLength, "null");
  347. return;
  348. }
  349. WriteValue (output, value.ToString ());
  350. }
  351. void WriteValue (StringBuilder output, string value)
  352. {
  353. if (String.IsNullOrEmpty (value)) {
  354. output.AppendCount (maxJsonLength, "\"\"");
  355. return;
  356. }
  357. output.AppendCount (maxJsonLength, "\"");
  358. char c;
  359. for (int i = 0; i < value.Length; i++) {
  360. c = value [i];
  361. switch (c) {
  362. case '\t':
  363. output.AppendCount (maxJsonLength, @"\t");
  364. break;
  365. case '\n':
  366. output.AppendCount (maxJsonLength, @"\n");
  367. break;
  368. case '\r':
  369. output.AppendCount (maxJsonLength, @"\r");
  370. break;
  371. case '\f':
  372. output.AppendCount (maxJsonLength, @"\f");
  373. break;
  374. case '\b':
  375. output.AppendCount (maxJsonLength, @"\b");
  376. break;
  377. case '<':
  378. output.AppendCount (maxJsonLength, @"\u003c");
  379. break;
  380. case '>':
  381. output.AppendCount (maxJsonLength, @"\u003e");
  382. break;
  383. case '"':
  384. output.AppendCount (maxJsonLength, "\\\"");
  385. break;
  386. case '\'':
  387. output.AppendCount (maxJsonLength, @"\u0027");
  388. break;
  389. case '\\':
  390. output.AppendCount (maxJsonLength, @"\\");
  391. break;
  392. default:
  393. if (c > '\u001f')
  394. output.AppendCount (maxJsonLength, c);
  395. else {
  396. output.Append("\\u00");
  397. int intVal = (int) c;
  398. output.AppendCount (maxJsonLength, (char) ('0' + (intVal >> 4)));
  399. intVal &= 0xf;
  400. output.AppendCount (maxJsonLength, (char) (intVal < 10 ? '0' + intVal : 'a' + (intVal - 10)));
  401. }
  402. break;
  403. }
  404. }
  405. output.AppendCount (maxJsonLength, "\"");
  406. }
  407. }
  408. }