JsonSerializer.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. // Copyright 2007 Konstantin Triger <[email protected]>
  4. //
  5. // Permission is hereby granted, free of charge, to any person
  6. // obtaining a copy of this software and associated documentation
  7. // files (the "Software"), to deal in the Software without
  8. // restriction, including without limitation the rights to use,
  9. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the
  11. // Software is furnished to do so, subject to the following
  12. // conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be
  15. // included in all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  24. // OTHER DEALINGS IN THE SOFTWARE.
  25. #endregion
  26. using System;
  27. using System.Collections.Generic;
  28. using System.Text;
  29. using System.IO;
  30. using System.Collections;
  31. using System.Reflection;
  32. using System.ComponentModel;
  33. using Newtonsoft.Json.Utilities;
  34. using System.Web.Script.Serialization;
  35. namespace Newtonsoft.Json
  36. {
  37. /// <summary>
  38. /// Specifies reference loop handling options for the <see cref="JsonWriter"/>.
  39. /// </summary>
  40. enum ReferenceLoopHandling
  41. {
  42. /// <summary>
  43. /// Throw a <see cref="JsonSerializationException"/> when a loop is encountered.
  44. /// </summary>
  45. Error = 0,
  46. /// <summary>
  47. /// Ignore loop references and do not serialize.
  48. /// </summary>
  49. Ignore = 1,
  50. /// <summary>
  51. /// Serialize loop references.
  52. /// </summary>
  53. Serialize = 2
  54. }
  55. /// <summary>
  56. /// Serializes and deserializes objects into and from the Json format.
  57. /// The <see cref="JsonSerializer"/> enables you to control how objects are encoded into Json.
  58. /// </summary>
  59. sealed class JsonSerializer
  60. {
  61. sealed internal class DeserializerLazyDictionary : JavaScriptSerializer.LazyDictionary
  62. {
  63. readonly JsonReader _reader;
  64. readonly JsonSerializer _serializer;
  65. IEnumerator<KeyValuePair<string, object>> _innerEnum;
  66. object _firstValue;
  67. public DeserializerLazyDictionary (JsonReader reader, JsonSerializer serializer) {
  68. _reader = reader;
  69. _serializer = serializer;
  70. }
  71. public object PeekFirst () {
  72. if (_innerEnum != null)
  73. throw new InvalidOperationException ("first already taken");
  74. _innerEnum = _serializer.PopulateObject (_reader);
  75. if (_innerEnum.MoveNext ())
  76. _firstValue = _innerEnum.Current;
  77. return _firstValue;
  78. }
  79. protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator () {
  80. if (_innerEnum == null)
  81. _innerEnum = _serializer.PopulateObject (_reader);
  82. if (_firstValue != null)
  83. yield return (KeyValuePair<string, object>) _firstValue;
  84. while (_innerEnum.MoveNext ())
  85. yield return _innerEnum.Current;
  86. }
  87. }
  88. sealed class SerializerLazyDictionary : JavaScriptSerializer.LazyDictionary
  89. {
  90. readonly object _source;
  91. public SerializerLazyDictionary (object source) {
  92. _source = source;
  93. }
  94. protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator () {
  95. KeyValuePair <string, object> kvpret;
  96. foreach (MemberInfo member in ReflectionUtils.GetFieldsAndProperties (_source.GetType (), BindingFlags.Public | BindingFlags.Instance)) {
  97. if (ReflectionUtils.CanReadMemberValue (member) && !member.IsDefined (typeof (ScriptIgnoreAttribute), true))
  98. if (!ReflectionUtils.IsIndexedProperty (member)) {
  99. // A temporary hack to prevent situations
  100. // when a type member cannot be serialized
  101. // for some reason (e.g. when serializing a
  102. // CultureInfo for 'en-US', processing its
  103. // Parent property which returns CultureInfo
  104. // for 'en' - asking for the Calendar
  105. // property value for that one will throw an
  106. // exception). Until a better solution is
  107. // devised, this has to stay in.
  108. try {
  109. kvpret = new KeyValuePair<string, object> (member.Name, ReflectionUtils.GetMemberValue (member, _source));
  110. } catch (Exception ex) {
  111. Console.Error.WriteLine ("HACK WARNING! NOT YIELDING THE VALUE! Serializing {0}.{1} threw an exception:", _source.GetType (), member.Name);
  112. Console.Error.WriteLine (ex);
  113. continue;
  114. }
  115. yield return kvpret;
  116. }
  117. }
  118. }
  119. }
  120. sealed class GenericDictionaryLazyDictionary : JavaScriptSerializer.LazyDictionary
  121. {
  122. readonly object _source;
  123. readonly PropertyInfo _piKeys;
  124. readonly PropertyInfo _piValues;
  125. public GenericDictionaryLazyDictionary (object source, Type dictType) {
  126. _source = source;
  127. _piKeys = dictType.GetProperty ("Keys");
  128. _piValues = dictType.GetProperty ("Values");
  129. }
  130. protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator () {
  131. IEnumerable eKeys = (IEnumerable) _piKeys.GetValue (_source, null);
  132. IEnumerator eValues = ((IEnumerable) _piValues.GetValue (_source, null)).GetEnumerator();
  133. foreach (object key in eKeys) {
  134. string keyString = key == null ? null : key.ToString ();
  135. if (!eValues.MoveNext ())
  136. throw new IndexOutOfRangeException (keyString);
  137. yield return new KeyValuePair<string, object> (keyString, eValues.Current);
  138. }
  139. if (eValues.MoveNext ())
  140. throw new IndexOutOfRangeException (eValues.Current != null ? eValues.Current.ToString () : String.Empty);
  141. }
  142. }
  143. private int _maxJsonLength;
  144. private int _recursionLimit;
  145. private int _currentRecursionCounter;
  146. private ReferenceLoopHandling _referenceLoopHandling;
  147. readonly JavaScriptSerializer _context;
  148. readonly JavaScriptTypeResolver _typeResolver;
  149. public int MaxJsonLength
  150. {
  151. get { return _maxJsonLength; }
  152. set { _maxJsonLength = value; }
  153. }
  154. public int RecursionLimit {
  155. get { return _recursionLimit; }
  156. set { _recursionLimit = value; }
  157. }
  158. /// <summary>
  159. /// Get or set how reference loops (e.g. a class referencing itself) is handled.
  160. /// </summary>
  161. public ReferenceLoopHandling ReferenceLoopHandling
  162. {
  163. get { return _referenceLoopHandling; }
  164. set
  165. {
  166. if (value < ReferenceLoopHandling.Error || value > ReferenceLoopHandling.Serialize)
  167. {
  168. throw new ArgumentOutOfRangeException("value");
  169. }
  170. _referenceLoopHandling = value;
  171. }
  172. }
  173. /// <summary>
  174. /// Initializes a new instance of the <see cref="JsonSerializer"/> class.
  175. /// </summary>
  176. public JsonSerializer(JavaScriptSerializer context, JavaScriptTypeResolver resolver)
  177. {
  178. _context = context;
  179. _typeResolver = resolver;
  180. _referenceLoopHandling = ReferenceLoopHandling.Error;
  181. }
  182. #region Deserialize
  183. public object Deserialize (TextReader reader) {
  184. return Deserialize (new JsonReader (reader, MaxJsonLength, RecursionLimit));
  185. }
  186. /// <summary>
  187. /// Deserializes the Json structure contained by the specified <see cref="JsonReader"/>
  188. /// into an instance of the specified type.
  189. /// </summary>
  190. /// <param name="reader">The type of object to create.</param>
  191. /// <param name="objectType">The <see cref="Type"/> of object being deserialized.</param>
  192. /// <returns>The instance of <paramref name="objectType"/> being deserialized.</returns>
  193. object Deserialize (JsonReader reader)
  194. {
  195. if (!reader.Read())
  196. return null;
  197. return GetObject(reader);
  198. }
  199. private object GetObject (JsonReader reader/*, Type objectType*/) {
  200. if (RecursionLimit > 0 && reader.CurrentRecursionLevel >= RecursionLimit) {
  201. throw new ArgumentException ("RecursionLimit exceeded.");
  202. }
  203. object value;
  204. switch (reader.TokenType) {
  205. // populate a typed object or generic dictionary/array
  206. // depending upon whether an objectType was supplied
  207. case JsonToken.StartObject:
  208. //value = PopulateObject(reader/*, objectType*/);
  209. value = new DeserializerLazyDictionary (reader, this);
  210. break;
  211. case JsonToken.StartArray:
  212. value = PopulateList (reader/*, objectType*/);
  213. break;
  214. case JsonToken.Integer:
  215. case JsonToken.Float:
  216. case JsonToken.String:
  217. case JsonToken.Boolean:
  218. case JsonToken.Date:
  219. //value = EnsureType(reader.Value, objectType);
  220. value = reader.Value;
  221. break;
  222. case JsonToken.Constructor:
  223. value = reader.Value.ToString ();
  224. break;
  225. case JsonToken.Null:
  226. case JsonToken.Undefined:
  227. value = null;
  228. break;
  229. default:
  230. throw new JsonSerializationException ("Unexpected token whil deserializing object: " + reader.TokenType);
  231. }
  232. return value;
  233. }
  234. private IEnumerable<object> PopulateList(JsonReader reader/*, Type objectType*/)
  235. {
  236. while (reader.Read())
  237. {
  238. switch (reader.TokenType)
  239. {
  240. case JsonToken.EndArray:
  241. yield break;
  242. case JsonToken.Comment:
  243. break;
  244. default:
  245. yield return GetObject(reader/*, elementType*/);
  246. break;
  247. }
  248. }
  249. throw new JsonSerializationException("Unexpected end when deserializing array.");
  250. }
  251. private IEnumerator<KeyValuePair<string, object>> PopulateObject (JsonReader reader/*, Type objectType*/)
  252. {
  253. reader.IncrementRecursionLevel ();
  254. while (reader.Read ())
  255. {
  256. switch (reader.TokenType)
  257. {
  258. case JsonToken.PropertyName:
  259. string memberName = reader.Value.ToString();
  260. if (!reader.Read ())
  261. throw new JsonSerializationException (string.Format ("Unexpected end when setting {0}'s value.", memberName));
  262. yield return new KeyValuePair<string, object> (memberName, GetObject (reader));
  263. break;
  264. case JsonToken.EndObject:
  265. reader.DecrementRecursionLevel ();
  266. yield break;
  267. default:
  268. throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
  269. }
  270. }
  271. throw new JsonSerializationException("Unexpected end when deserializing object.");
  272. }
  273. #endregion
  274. #region Serialize
  275. /// <summary>
  276. /// Serializes the specified <see cref="Object"/> and writes the Json structure
  277. /// to a <c>Stream</c> using the specified <see cref="TextWriter"/>.
  278. /// </summary>
  279. /// <param name="textWriter">The <see cref="TextWriter"/> used to write the Json structure.</param>
  280. /// <param name="value">The <see cref="Object"/> to serialize.</param>
  281. public void Serialize(TextWriter textWriter, object value)
  282. {
  283. Serialize(new JsonWriter(textWriter, MaxJsonLength), value);
  284. }
  285. /// <summary>
  286. /// Serializes the specified <see cref="Object"/> and writes the Json structure
  287. /// to a <c>Stream</c> using the specified <see cref="JsonWriter"/>.
  288. /// </summary>
  289. /// <param name="jsonWriter">The <see cref="JsonWriter"/> used to write the Json structure.</param>
  290. /// <param name="value">The <see cref="Object"/> to serialize.</param>
  291. void Serialize(JsonWriter jsonWriter, object value)
  292. {
  293. SerializeValue (jsonWriter, value, true, null);
  294. }
  295. private void SerializeValue(JsonWriter writer, object value)
  296. {
  297. SerializeValue (writer, value, false, null);
  298. }
  299. private void WritePropertyName (JsonWriter writer, string name)
  300. {
  301. if (String.IsNullOrEmpty (name))
  302. return;
  303. writer.WritePropertyName (name);
  304. }
  305. private void SerializeValue (JsonWriter writer, object value, bool topLevelObject, string propertyName)
  306. {
  307. //JsonConverter converter;
  308. _currentRecursionCounter++;
  309. if (RecursionLimit > 0 && _currentRecursionCounter > RecursionLimit) {
  310. throw new ArgumentException ("RecursionLimit exceeded.");
  311. }
  312. if (value == null) {
  313. WritePropertyName (writer, propertyName);
  314. writer.WriteNull ();
  315. }
  316. else {
  317. Type valueType = value.GetType ();
  318. JavaScriptConverter jsconverter = _context.GetConverter (valueType);
  319. if (jsconverter != null) {
  320. value = jsconverter.Serialize (value, _context);
  321. if (value == null) {
  322. WritePropertyName (writer, propertyName);
  323. writer.WriteNull ();
  324. _currentRecursionCounter--;
  325. return;
  326. }
  327. }
  328. switch (Type.GetTypeCode (valueType)) {
  329. case TypeCode.String:
  330. WritePropertyName (writer, propertyName);
  331. writer.WriteValue ((string) value);
  332. break;
  333. case TypeCode.Char:
  334. WritePropertyName (writer, propertyName);
  335. writer.WriteValue ((char) value);
  336. break;
  337. case TypeCode.Boolean:
  338. WritePropertyName (writer, propertyName);
  339. writer.WriteValue ((bool) value);
  340. break;
  341. case TypeCode.SByte:
  342. WritePropertyName (writer, propertyName);
  343. writer.WriteValue ((sbyte) value);
  344. break;
  345. case TypeCode.Int16:
  346. WritePropertyName (writer, propertyName);
  347. writer.WriteValue ((short) value);
  348. break;
  349. case TypeCode.UInt16:
  350. WritePropertyName (writer, propertyName);
  351. writer.WriteValue ((ushort) value);
  352. break;
  353. case TypeCode.Int32:
  354. WritePropertyName (writer, propertyName);
  355. writer.WriteValue ((int) value);
  356. break;
  357. case TypeCode.Byte:
  358. WritePropertyName (writer, propertyName);
  359. writer.WriteValue ((byte) value);
  360. break;
  361. case TypeCode.UInt32:
  362. WritePropertyName (writer, propertyName);
  363. writer.WriteValue ((uint) value);
  364. break;
  365. case TypeCode.Int64:
  366. WritePropertyName (writer, propertyName);
  367. writer.WriteValue ((long) value);
  368. break;
  369. case TypeCode.UInt64:
  370. WritePropertyName (writer, propertyName);
  371. writer.WriteValue ((ulong) value);
  372. break;
  373. case TypeCode.Single:
  374. WritePropertyName (writer, propertyName);
  375. writer.WriteValue ((float) value);
  376. break;
  377. case TypeCode.Double:
  378. WritePropertyName (writer, propertyName);
  379. writer.WriteValue ((double) value);
  380. break;
  381. case TypeCode.DateTime:
  382. WritePropertyName (writer, propertyName);
  383. writer.WriteValue ((DateTime) value);
  384. break;
  385. case TypeCode.Decimal:
  386. WritePropertyName (writer, propertyName);
  387. writer.WriteValue ((decimal) value);
  388. break;
  389. default:
  390. ThrowOnReferenceLoop (writer, value);
  391. writer.SerializeStack.Push (value);
  392. try {
  393. Type genDictType;
  394. if (value is IDictionary) {
  395. WritePropertyName (writer, propertyName);
  396. SerializeDictionary (writer, (IDictionary) value);
  397. } else if (value is IDictionary<string, object>) {
  398. WritePropertyName (writer, propertyName);
  399. SerializeDictionary (writer, (IDictionary<string, object>) value, null);
  400. } else if ((genDictType = ReflectionUtils.GetGenericDictionary (valueType)) != null) {
  401. WritePropertyName (writer, propertyName);
  402. SerializeDictionary (writer, new GenericDictionaryLazyDictionary (value, genDictType), null);
  403. } else if (value is IEnumerable) {
  404. WritePropertyName (writer, propertyName);
  405. SerializeEnumerable (writer, (IEnumerable) value);
  406. } else if (topLevelObject) {
  407. SerializeCustomObject (writer, value, valueType);
  408. }
  409. }
  410. finally {
  411. object x = writer.SerializeStack.Pop ();
  412. if (x != value)
  413. throw new InvalidOperationException ("Serialization stack is corrupted");
  414. }
  415. break;
  416. }
  417. }
  418. _currentRecursionCounter--;
  419. }
  420. private void ThrowOnReferenceLoop (JsonWriter writer, object value)
  421. {
  422. switch (_referenceLoopHandling) {
  423. case ReferenceLoopHandling.Error:
  424. if (writer.SerializeStack.Contains (value))
  425. throw new JsonSerializationException ("Self referencing loop");
  426. break;
  427. case ReferenceLoopHandling.Ignore:
  428. // return from method
  429. return;
  430. case ReferenceLoopHandling.Serialize:
  431. // continue
  432. break;
  433. default:
  434. throw new InvalidOperationException (string.Format ("Unexpected ReferenceLoopHandling value: '{0}'", _referenceLoopHandling));
  435. }
  436. }
  437. private void SerializeEnumerable (JsonWriter writer, IEnumerable values) {
  438. writer.WriteStartArray ();
  439. foreach (object value in values)
  440. SerializeValue (writer, value, true, null);
  441. writer.WriteEndArray ();
  442. }
  443. private void SerializeDictionary(JsonWriter writer, IDictionary values)
  444. {
  445. writer.WriteStartObject();
  446. foreach (DictionaryEntry entry in values)
  447. SerializePair (writer, entry.Key.ToString (), entry.Value);
  448. writer.WriteEndObject();
  449. }
  450. private void SerializeDictionary (JsonWriter writer, IDictionary<string, object> values, string typeID) {
  451. writer.WriteStartObject ();
  452. if (typeID != null) {
  453. SerializePair (writer, JavaScriptSerializer.SerializedTypeNameKey, typeID);
  454. }
  455. foreach (KeyValuePair<string, object> entry in values)
  456. SerializePair (writer, entry.Key, entry.Value);
  457. writer.WriteEndObject ();
  458. }
  459. private void SerializeCustomObject (JsonWriter writer, object value, Type valueType)
  460. {
  461. if (value is Uri) {
  462. Uri uri = value as Uri;
  463. writer.WriteValue (uri.GetComponents (UriComponents.AbsoluteUri, UriFormat.UriEscaped));
  464. return;
  465. }
  466. if (valueType == typeof (Guid)) {
  467. writer.WriteValue (((Guid) value).ToString ());
  468. return;
  469. }
  470. string typeID = null;
  471. if (_typeResolver != null) {
  472. typeID = _typeResolver.ResolveTypeId (valueType);
  473. }
  474. SerializeDictionary (writer, new SerializerLazyDictionary (value), typeID);
  475. }
  476. private void SerializePair (JsonWriter writer, string key, object value) {
  477. SerializeValue (writer, value, false, key);
  478. }
  479. #endregion
  480. }
  481. }