1 //----------------------------------------------------------------------- 2 // <copyright file="SimpleJson.cs" company="The Outercurve Foundation"> 3 // Copyright (c) 2011, The Outercurve Foundation. 4 // Copyright (c) 2012, Antmicro <www.antmicro.com> 5 // 6 // Licensed under the MIT License (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // http://www.opensource.org/licenses/mit-license.php 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // </copyright> 17 // <author>Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me), Piotr Zierhoffer (antmicro.com)</author> 18 // <website>https://github.com/facebook-csharp-sdk/simple-json</website> 19 //----------------------------------------------------------------------- 20 21 // NOTE: uncomment the following line to make SimpleJson class internal. 22 #define SIMPLE_JSON_INTERNAL 23 24 // NOTE: uncomment the following line to make JsonArray and JsonObject class internal. 25 #define SIMPLE_JSON_OBJARRAYINTERNAL 26 27 // NOTE: uncomment the following line to enable dynamic support. 28 #define SIMPLE_JSON_DYNAMIC 29 30 // original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html 31 using System; 32 using System.Collections; 33 using System.Collections.Generic; 34 using System.ComponentModel; 35 using System.Linq; 36 using System.Diagnostics.CodeAnalysis; 37 using System.Dynamic; 38 using System.Globalization; 39 using System.Reflection; 40 using System.Text; 41 using Antmicro.Renode.Config.Reflection; 42 43 namespace Antmicro.Renode.Config 44 { 45 #region JsonArray 46 47 /// <summary> 48 /// Represents the json array. 49 /// </summary> 50 [EditorBrowsable(EditorBrowsableState.Never)] 51 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] 52 53 public class JsonArray : List<object> 54 { 55 /// <summary> 56 /// Initializes a new instance of the <see cref="JsonArray"/> class. 57 /// </summary> JsonArray()58 public JsonArray() 59 { 60 } 61 62 /// <summary> 63 /// Returns the array of T if every element is T, array of object otherwise. 64 /// </summary> 65 /// <returns> 66 /// This array, but strongly typed if possible. 67 /// </returns> ToDynamic()68 public dynamic ToDynamic() 69 { 70 if(this.Count > 0) 71 { 72 return Dynamize((dynamic)this[0]); 73 } 74 return this; 75 } 76 Dynamize(T elem)77 private dynamic Dynamize<T>(T elem) 78 { 79 if(this.All(x => x is T)) 80 { 81 return new List<T>(this.Cast<T>()); 82 } 83 return this; 84 } 85 86 /// <summary> 87 /// Initializes a new instance of the <see cref="JsonArray"/> class. 88 /// </summary> 89 /// <param name="capacity">The capacity of the json array.</param> JsonArray(int capacity)90 public JsonArray(int capacity) : base(capacity) 91 { 92 } 93 94 /// <summary> 95 /// The json representation of the array. 96 /// </summary> 97 /// <returns>The json representation of the array.</returns> ToString()98 public override string ToString() 99 { 100 return SimpleJson.SerializeObject(this) ?? string.Empty; 101 } 102 } 103 104 #endregion 105 106 #region JsonObject 107 108 internal static class HashSetExtensions 109 { Add(this HashSet<KeyValuePair<K, object>> hashSet, K key, object value)110 public static void Add<K>(this HashSet<KeyValuePair<K, object>> hashSet, K key, object value) 111 { 112 hashSet.Add(new KeyValuePair<K, object>(key, value)); 113 } 114 ContainsKey(this HashSet<KeyValuePair<K, object>> hashSet, K key)115 public static bool ContainsKey<K>(this HashSet<KeyValuePair<K, object>> hashSet, K key) 116 { 117 return hashSet.Any(x => x.Key.Equals(key)); 118 } 119 Keys(this HashSet<KeyValuePair<K, object>> hashSet)120 public static ICollection<K> Keys<K>(this HashSet<KeyValuePair<K, object>> hashSet) 121 { 122 return hashSet.Select(x => x.Key).ToList(); 123 } 124 Remove(this HashSet<KeyValuePair<K, object>> hashSet, K key)125 public static bool Remove<K>(this HashSet<KeyValuePair<K, object>> hashSet, K key) 126 { 127 return hashSet.RemoveWhere(x => x.Key.Equals(key)) > 0; 128 } 129 RemoveFirst(this HashSet<KeyValuePair<K, object>> hashSet, K key)130 public static bool RemoveFirst<K>(this HashSet<KeyValuePair<K, object>> hashSet, K key) 131 { 132 var found = false; 133 return hashSet.RemoveWhere(x => {if(!found && x.Key.Equals(key)) {found = true; return true;} return false;}) > 0; 134 } 135 TryGetValue(this HashSet<KeyValuePair<K, object>> hashSet, K key, out object value)136 public static bool TryGetValue<K>(this HashSet<KeyValuePair<K, object>> hashSet, K key, out object value) 137 { 138 var valueEntry = hashSet.FirstOrDefault(x => x.Key.Equals(key)); 139 if(valueEntry.Equals(default(KeyValuePair<K, object>))) 140 { 141 value = valueEntry.Value; 142 return true; 143 } 144 value = null; 145 return false; 146 } 147 Get(this HashSet<KeyValuePair<K, object>> hashSet, K key)148 public static object Get<K>(this HashSet<KeyValuePair<K, object>> hashSet, K key) 149 { 150 return hashSet.First(x => x.Key.Equals(key)).Value; 151 } 152 Set(this HashSet<KeyValuePair<K, object>> hashSet, K key, object value)153 public static void Set<K>(this HashSet<KeyValuePair<K, object>> hashSet, K key, object value) 154 { 155 hashSet.RemoveFirst(key); 156 hashSet.Add(key, value); 157 } 158 Values(this HashSet<KeyValuePair<K, object>> hashSet)159 public static ICollection<object> Values<K>(this HashSet<KeyValuePair<K, object>> hashSet) 160 { 161 return hashSet.Select(x => x.Value).ToList(); 162 } 163 } 164 165 /// <summary> 166 /// Represents the json object. 167 /// </summary> 168 [EditorBrowsable(EditorBrowsableState.Never)] 169 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] 170 internal class JsonObject : DynamicObject, IDictionary<string, object> 171 { 172 /// <summary> 173 /// The internal member dictionary. 174 /// </summary> 175 private readonly HashSet<KeyValuePair<string, object>> _members = new HashSet<KeyValuePair<string, object>>(); 176 177 /// <summary> 178 /// Gets the <see cref="System.Object"/> at the specified index. 179 /// </summary> 180 /// <value></value> 181 public object this[int index] 182 { 183 get { return GetAtIndex(_members, index); } 184 } 185 GetAtIndex(HashSet<KeyValuePair<string, object>> obj, int index)186 internal static object GetAtIndex(HashSet<KeyValuePair<string, object>> obj, int index) 187 { 188 if(obj == null) 189 { 190 throw new ArgumentNullException("obj"); 191 } 192 193 if(index >= obj.Count) 194 { 195 throw new ArgumentOutOfRangeException("index"); 196 } 197 198 int i = 0; 199 foreach(KeyValuePair<string, object> o in obj) 200 if(i++ == index) 201 { 202 return o.Value; 203 } 204 205 return null; 206 } 207 208 /// <summary> 209 /// Adds the specified key. 210 /// </summary> 211 /// <param name="key">The key.</param> 212 /// <param name="value">The value.</param> Add(string key, object value)213 public void Add(string key, object value) 214 { 215 _members.Add(key, value); 216 } 217 218 /// <summary> 219 /// Determines whether the specified key contains key. 220 /// </summary> 221 /// <param name="key">The key.</param> 222 /// <returns> 223 /// <c>true</c> if the specified key contains key; otherwise, <c>false</c>. 224 /// </returns> ContainsKey(string key)225 public bool ContainsKey(string key) 226 { 227 return _members.ContainsKey(key); 228 } 229 230 /// <summary> 231 /// Gets the keys. 232 /// </summary> 233 /// <value>The keys.</value> 234 public ICollection<string> Keys 235 { 236 get { return _members.Keys(); } 237 } 238 239 /// <summary> 240 /// Removes the specified key. 241 /// </summary> 242 /// <param name="key">The key.</param> 243 /// <returns></returns> Remove(string key)244 public bool Remove(string key) 245 { 246 return _members.Remove(key); 247 } 248 249 /// <summary> 250 /// Tries the get value. 251 /// </summary> 252 /// <param name="key">The key.</param> 253 /// <param name="value">The value.</param> 254 /// <returns></returns> TryGetValue(string key, out object value)255 public bool TryGetValue(string key, out object value) 256 { 257 return _members.TryGetValue(key, out value); 258 } 259 260 /// <summary> 261 /// Gets the values. 262 /// </summary> 263 /// <value>The values.</value> 264 public ICollection<object> Values 265 { 266 get { return _members.Values(); } 267 } 268 269 /// <summary> 270 /// Gets or sets the <see cref="System.Object"/> with the specified key. 271 /// </summary> 272 /// <value></value> 273 public object this[string key] 274 { 275 get { return _members.Get(key); } 276 set { _members.Set(key, value); } 277 } 278 279 /// <summary> 280 /// Adds the specified item. 281 /// </summary> 282 /// <param name="item">The item.</param> Add(KeyValuePair<string, object> item)283 public void Add(KeyValuePair<string, object> item) 284 { 285 _members.Add(item.Key, item.Value); 286 } 287 288 /// <summary> 289 /// Clears this instance. 290 /// </summary> Clear()291 public void Clear() 292 { 293 _members.Clear(); 294 } 295 296 /// <summary> 297 /// Determines whether [contains] [the specified item]. 298 /// </summary> 299 /// <param name="item">The item.</param> 300 /// <returns> 301 /// <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>. 302 /// </returns> Contains(KeyValuePair<string, object> item)303 public bool Contains(KeyValuePair<string, object> item) 304 { 305 return _members.ContainsKey(item.Key) && _members.Get(item.Key) == item.Value; 306 } 307 308 /// <summary> 309 /// Copies to. 310 /// </summary> 311 /// <param name="array">The array.</param> 312 /// <param name="arrayIndex">Index of the array.</param> CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)313 public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) 314 { 315 int num = Count; 316 foreach(KeyValuePair<string, object> kvp in this) 317 { 318 array[arrayIndex++] = kvp; 319 320 if(--num <= 0) 321 { 322 return; 323 } 324 } 325 } 326 327 /// <summary> 328 /// Gets the count. 329 /// </summary> 330 /// <value>The count.</value> 331 public int Count 332 { 333 get { return _members.Count; } 334 } 335 336 /// <summary> 337 /// Gets a value indicating whether this instance is read only. 338 /// </summary> 339 /// <value> 340 /// <c>true</c> if this instance is read only; otherwise, <c>false</c>. 341 /// </value> 342 public bool IsReadOnly 343 { 344 get { return false; } 345 } 346 347 /// <summary> 348 /// Removes the specified item. 349 /// </summary> 350 /// <param name="item">The item.</param> 351 /// <returns></returns> Remove(KeyValuePair<string, object> item)352 public bool Remove(KeyValuePair<string, object> item) 353 { 354 return _members.Remove(item.Key); 355 } 356 357 /// <summary> 358 /// Gets the enumerator. 359 /// </summary> 360 /// <returns></returns> GetEnumerator()361 public IEnumerator<KeyValuePair<string, object>> GetEnumerator() 362 { 363 return _members.GetEnumerator(); 364 } 365 366 /// <summary> 367 /// Returns an enumerator that iterates through a collection. 368 /// </summary> 369 /// <returns> 370 /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection. 371 /// </returns> IEnumerable.GetEnumerator()372 IEnumerator IEnumerable.GetEnumerator() 373 { 374 return _members.GetEnumerator(); 375 } 376 377 /// <summary> 378 /// Returns a json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. 379 /// </summary> 380 /// <returns> 381 /// A json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. 382 /// </returns> ToString()383 public override string ToString() 384 { 385 return SimpleJson.SerializeObject(this); 386 } 387 388 /// <summary> 389 /// Provides implementation for type conversion operations. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that convert an object from one type to another. 390 /// </summary> 391 /// <param name="binder">Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Type returns the <see cref="T:System.String"/> type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion.</param> 392 /// <param name="result">The result of the type conversion operation.</param> 393 /// <returns> 394 /// Alwasy returns true. 395 /// </returns> TryConvert(ConvertBinder binder, out object result)396 public override bool TryConvert(ConvertBinder binder, out object result) 397 { 398 // <pex> 399 if(binder == (ConvertBinder)null) 400 { 401 throw new ArgumentNullException("binder"); 402 } 403 // </pex> 404 Type targetType = binder.Type; 405 406 if((targetType == typeof(IEnumerable)) || 407 (targetType == typeof(IEnumerable<KeyValuePair<string, object>>)) || 408 (targetType == typeof(IDictionary<string, object>)) || 409 (targetType == typeof(IDictionary))) 410 { 411 result = this; 412 return true; 413 } 414 415 return base.TryConvert(binder, out result); 416 } 417 418 /// <summary> 419 /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. 420 /// </summary> 421 /// <param name="binder">Provides information about the deletion.</param> 422 /// <returns> 423 /// Alwasy returns true. 424 /// </returns> TryDeleteMember(DeleteMemberBinder binder)425 public override bool TryDeleteMember(DeleteMemberBinder binder) 426 { 427 // <pex> 428 if(binder == (DeleteMemberBinder)null) 429 { 430 throw new ArgumentNullException("binder"); 431 } 432 // </pex> 433 return _members.Remove(binder.Name); 434 } 435 436 /// <summary> 437 /// Provides the implementation for operations that get a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for indexing operations. 438 /// </summary> 439 /// <param name="binder">Provides information about the operation.</param> 440 /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, <paramref name="indexes"/> is equal to 3.</param> 441 /// <param name="result">The result of the index operation.</param> 442 /// <returns> 443 /// Alwasy returns true. 444 /// </returns> TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)445 public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) 446 { 447 if(indexes.Length == 1) 448 { 449 result = ((IDictionary<string, object>)this)[(string)indexes[0]]; 450 return true; 451 } 452 result = (object)null; 453 return true; 454 } 455 456 /// <summary> 457 /// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as getting a value for a property. 458 /// </summary> 459 /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param> 460 /// <param name="result">The result of the get operation. For example, if the method is called for a property, you can assign the property value to <paramref name="result"/>.</param> 461 /// <returns> 462 /// Alwasy returns true. 463 /// </returns> TryGetMember(GetMemberBinder binder, out object result)464 public override bool TryGetMember(GetMemberBinder binder, out object result) 465 { 466 object value; 467 if(_members.TryGetValue(binder.Name, out value)) 468 { 469 result = value; 470 return true; 471 } 472 result = (object)null; 473 return true; 474 } 475 476 /// <summary> 477 /// Provides the implementation for operations that set a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that access objects by a specified index. 478 /// </summary> 479 /// <param name="binder">Provides information about the operation.</param> 480 /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="indexes"/> is equal to 3.</param> 481 /// <param name="value">The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="value"/> is equal to 10.</param> 482 /// <returns> 483 /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. 484 /// </returns> TrySetIndex(SetIndexBinder binder, object[] indexes, object value)485 public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) 486 { 487 if(indexes.Length == 1) 488 { 489 ((IDictionary<string, object>)this)[(string)indexes[0]] = value; 490 return true; 491 } 492 493 return base.TrySetIndex(binder, indexes, value); 494 } 495 496 /// <summary> 497 /// Provides the implementation for operations that set member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as setting a value for a property. 498 /// </summary> 499 /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param> 500 /// <param name="value">The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, the <paramref name="value"/> is "Test".</param> 501 /// <returns> 502 /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) 503 /// </returns> TrySetMember(SetMemberBinder binder, object value)504 public override bool TrySetMember(SetMemberBinder binder, object value) 505 { 506 // <pex> 507 if(binder == (SetMemberBinder)null) 508 { 509 throw new ArgumentNullException("binder"); 510 } 511 // </pex> 512 _members.Set(binder.Name, value); 513 return true; 514 } 515 516 /// <summary> 517 /// Returns the enumeration of all dynamic member names. 518 /// </summary> 519 /// <returns> 520 /// A sequence that contains dynamic member names. 521 /// </returns> GetDynamicMemberNames()522 public override IEnumerable<string> GetDynamicMemberNames() 523 { 524 foreach(var key in Keys) 525 yield return key; 526 } 527 } 528 529 #endregion 530 } 531 532 namespace Antmicro.Renode.Config 533 { 534 #region JsonParser 535 536 /// <summary> 537 /// This class encodes and decodes JSON strings. 538 /// Spec. details, see http://www.json.org/ 539 /// 540 /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). 541 /// All numbers are parsed to doubles. 542 /// </summary> 543 public class SimpleJson 544 { 545 private const int TOKEN_NONE = 0; 546 private const int TOKEN_CURLY_OPEN = 1; 547 private const int TOKEN_CURLY_CLOSE = 2; 548 private const int TOKEN_SQUARED_OPEN = 3; 549 private const int TOKEN_SQUARED_CLOSE = 4; 550 private const int TOKEN_COLON = 5; 551 private const int TOKEN_COMMA = 6; 552 private const int TOKEN_STRING = 7; 553 private const int TOKEN_NUMBER = 8; 554 private const int TOKEN_TRUE = 9; 555 private const int TOKEN_FALSE = 10; 556 private const int TOKEN_NULL = 11; 557 private const int BUILDER_CAPACITY = 2000; 558 559 /// <summary> 560 /// Parses the string json into a value 561 /// </summary> 562 /// <param name="json">A JSON string.</param> 563 /// <returns>An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false</returns> DeserializeObject(string json)564 public static object DeserializeObject (string json) 565 { 566 object @object; 567 int line = 1; 568 if(TryDeserializeObject(json, out @object, ref line)) 569 { 570 return @object; 571 } 572 var numOfLines = json.Count (x=> x == '\n'); 573 if(line > numOfLines) 574 { 575 throw new System.Runtime.Serialization.SerializationException(String.Format("Invalid JSON string. Probably a closing brace is missing.")); 576 } 577 throw new System.Runtime.Serialization.SerializationException(String.Format("Invalid JSON string on line {0}.", line)); 578 } 579 580 /// <summary> 581 /// Try parsing the json string into a value. 582 /// </summary> 583 /// <param name="json"> 584 /// A JSON string. 585 /// </param> 586 /// <param name="object"> 587 /// The object. 588 /// </param> 589 /// <returns> 590 /// Returns true if successfull otherwise false. 591 /// </returns> TryDeserializeObject(string json, out object @object, ref int line)592 public static bool TryDeserializeObject(string json, out object @object, ref int line) 593 { 594 bool success = true; 595 if(json != null) 596 { 597 char[] charArray = json.ToCharArray(); 598 int index = 0; 599 @object = ParseValue(charArray, ref index, ref success, ref line); 600 EatWhitespace(charArray, ref index, ref line); 601 EatComment(charArray, ref index, ref line); 602 if(index != charArray.Length) 603 { 604 //json ends too quickly 605 success = false; 606 } 607 } 608 else 609 { 610 @object = null; 611 } 612 613 return success; 614 } 615 DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy)616 public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) 617 { 618 object jsonObject = DeserializeObject(json); 619 620 return type == null || jsonObject != null && 621 622 jsonObject.GetType().IsAssignableFrom(type) ? jsonObject 623 : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); 624 } 625 DeserializeObject(string json, Type type)626 public static object DeserializeObject(string json, Type type) 627 { 628 return DeserializeObject(json, type, null); 629 } 630 DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy)631 public static T DeserializeObject<T>(string json, IJsonSerializerStrategy jsonSerializerStrategy) 632 { 633 return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); 634 } 635 DeserializeObject(string json)636 public static T DeserializeObject<T>(string json) 637 { 638 return (T)DeserializeObject(json, typeof(T), null); 639 } 640 641 /// <summary> 642 /// Converts a IDictionary<string,object> / IList<object> object into a JSON string 643 /// </summary> 644 /// <param name="json">A IDictionary<string,object> / IList<object></param> 645 /// <param name="jsonSerializerStrategy">Serializer strategy to use</param> 646 /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns> SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy)647 public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) 648 { 649 StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); 650 bool success = SerializeValue(jsonSerializerStrategy, json, builder); 651 return (success ? builder.ToString() : null); 652 } 653 SerializeObject(object json)654 public static string SerializeObject(object json) 655 { 656 return SerializeObject(json, CurrentJsonSerializerStrategy); 657 } 658 EscapeToJavascriptString(string jsonString)659 public static string EscapeToJavascriptString(string jsonString) 660 { 661 if(string.IsNullOrEmpty(jsonString)) 662 { 663 return jsonString; 664 } 665 666 StringBuilder sb = new StringBuilder(); 667 char c; 668 669 for(int i = 0; i < jsonString.Length;) 670 { 671 c = jsonString[i++]; 672 673 if(c == '\\') 674 { 675 int remainingLength = jsonString.Length - i; 676 if(remainingLength >= 2) 677 { 678 char lookahead = jsonString[i]; 679 if(lookahead == '\\') 680 { 681 sb.Append('\\'); 682 ++i; 683 } 684 else 685 if(lookahead == '"') 686 { 687 sb.Append("\""); 688 ++i; 689 } 690 else 691 if(lookahead == 't') 692 { 693 sb.Append('\t'); 694 ++i; 695 } 696 else 697 if(lookahead == 'b') 698 { 699 sb.Append('\b'); 700 ++i; 701 } 702 else 703 if(lookahead == 'n') 704 { 705 sb.Append('\n'); 706 ++i; 707 } 708 else 709 if(lookahead == 'r') 710 { 711 sb.Append('\r'); 712 ++i; 713 } 714 } 715 } 716 else 717 { 718 sb.Append(c); 719 } 720 } 721 722 return sb.ToString(); 723 } 724 ParseObject(char[] json, ref int index, ref bool success, ref int line)725 protected static IDictionary<string, object> ParseObject(char[] json, ref int index, ref bool success, ref int line) 726 { 727 IDictionary<string, object> table = new JsonObject(); 728 int token; 729 730 // { 731 NextToken(json, ref index, ref line); 732 733 bool done = false; 734 while(!done) 735 { 736 token = LookAhead(json, index, line); 737 if(token == TOKEN_NONE) 738 { 739 success = false; 740 return null; 741 } 742 else 743 if(token == TOKEN_COMMA) 744 { 745 NextToken(json, ref index, ref line); 746 } 747 else 748 if(token == TOKEN_CURLY_CLOSE) 749 { 750 NextToken(json, ref index, ref line); 751 return table; 752 } 753 else 754 { 755 // name 756 string name = ParseString(json, ref index, ref success, ref line); 757 if(!success) 758 { 759 return null; 760 } 761 762 // : 763 token = NextToken(json, ref index, ref line); 764 if(token != TOKEN_COLON) 765 { 766 success = false; 767 return null; 768 } 769 770 // value 771 object value = ParseValue(json, ref index, ref success, ref line); 772 if(!success) 773 { 774 return null; 775 } 776 777 int nextToken = LookAhead(json, index, line); 778 if(nextToken != TOKEN_COMMA && nextToken != TOKEN_CURLY_CLOSE) 779 { 780 success = false; 781 return null; 782 } 783 784 table.Add(name, value); 785 } 786 } 787 788 return table; 789 } 790 ParseArray(char[] json, ref int index, ref bool success, ref int line)791 protected static JsonArray ParseArray(char[] json, ref int index, ref bool success, ref int line) 792 { 793 JsonArray array = new JsonArray(); 794 795 // [ 796 NextToken(json, ref index, ref line); 797 798 bool done = false; 799 while(!done) 800 { 801 int token = LookAhead(json, index, line); 802 if(token == TOKEN_NONE) 803 { 804 success = false; 805 return null; 806 } 807 else 808 if(token == TOKEN_COMMA) 809 { 810 NextToken(json, ref index, ref line); 811 } 812 else 813 if(token == TOKEN_SQUARED_CLOSE) 814 { 815 NextToken(json, ref index, ref line); 816 break; 817 } 818 else 819 { 820 object value = ParseValue(json, ref index, ref success, ref line); 821 if(!success) 822 { 823 return null; 824 } 825 826 int nextToken = LookAhead(json, index, line); 827 if(nextToken != TOKEN_COMMA && nextToken != TOKEN_SQUARED_CLOSE) 828 { 829 return null; 830 } 831 832 array.Add(value); 833 } 834 } 835 836 return array; 837 } 838 ParseValue(char[] json, ref int index, ref bool success, ref int line)839 protected static object ParseValue(char[] json, ref int index, ref bool success, ref int line) 840 { 841 switch(LookAhead(json, index, line)) 842 { 843 case TOKEN_STRING: 844 return ParseString(json, ref index, ref success, ref line); 845 case TOKEN_NUMBER: 846 return ParseNumber(json, ref index, ref success, ref line); 847 case TOKEN_CURLY_OPEN: 848 return ParseObject(json, ref index, ref success, ref line); 849 case TOKEN_SQUARED_OPEN: 850 return ParseArray(json, ref index, ref success, ref line); 851 case TOKEN_TRUE: 852 NextToken(json, ref index, ref line); 853 return true; 854 case TOKEN_FALSE: 855 NextToken(json, ref index, ref line); 856 return false; 857 case TOKEN_NULL: 858 NextToken(json, ref index, ref line); 859 return null; 860 case TOKEN_NONE: 861 break; 862 } 863 864 success = false; 865 return null; 866 } 867 ParseString(char[] json, ref int index, ref bool success, ref int line)868 protected static string ParseString(char[] json, ref int index, ref bool success, ref int line) 869 { 870 StringBuilder s = new StringBuilder(BUILDER_CAPACITY); 871 char c; 872 873 EatWhitespace(json, ref index, ref line); 874 EatComment(json, ref index, ref line); 875 876 // "s 877 c = json[index++]; 878 879 bool complete = false; 880 while(!complete) 881 { 882 if(index == json.Length) 883 { 884 break; 885 } 886 887 c = json[index++]; 888 if(c == '"') 889 { 890 complete = true; 891 break; 892 } 893 else 894 if(c == '\\') 895 { 896 if(index == json.Length) 897 { 898 break; 899 } 900 c = json[index++]; 901 if(c == '"') 902 { 903 s.Append('"'); 904 } 905 else 906 if(c == '\\') 907 { 908 s.Append('\\'); 909 } 910 else 911 if(c == '/') 912 { 913 s.Append('/'); 914 } 915 else 916 if(c == 'b') 917 { 918 s.Append('\b'); 919 } 920 else 921 if(c == 'f') 922 { 923 s.Append('\f'); 924 } 925 else 926 if(c == 'n') 927 { 928 s.Append('\n'); 929 } 930 else 931 if(c == 'r') 932 { 933 s.Append('\r'); 934 } 935 else 936 if(c == 't') 937 { 938 s.Append('\t'); 939 } 940 else 941 if(c == 'u') 942 { 943 int remainingLength = json.Length - index; 944 if(remainingLength >= 4) 945 { 946 // parse the 32 bit hex into an integer codepoint 947 uint codePoint; 948 if( 949 !(success = 950 UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, 951 CultureInfo.InvariantCulture, out codePoint))) 952 { 953 return ""; 954 } 955 956 // convert the integer codepoint to a unicode char and add to string 957 958 if(0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate 959 { 960 index += 4; // skip 4 chars 961 remainingLength = json.Length - index; 962 if(remainingLength >= 6) 963 { 964 uint lowCodePoint; 965 if(new string(json, index, 2) == "\\u" && 966 UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, 967 CultureInfo.InvariantCulture, out lowCodePoint)) 968 { 969 if(0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate 970 { 971 s.Append((char)codePoint); 972 s.Append((char)lowCodePoint); 973 index += 6; // skip 6 chars 974 continue; 975 } 976 } 977 } 978 success = false; // invalid surrogate pair 979 return ""; 980 } 981 s.Append(Char.ConvertFromUtf32((int)codePoint)); 982 // skip 4 chars 983 index += 4; 984 } 985 else 986 { 987 break; 988 } 989 } 990 } 991 else 992 { 993 s.Append(c); 994 } 995 } 996 997 if(!complete) 998 { 999 success = false; 1000 return null; 1001 } 1002 1003 return s.ToString(); 1004 } 1005 1006 private static char[] HexArray = "aAbBcCdDeEfFxX".ToCharArray(); 1007 ParseNumber(char[] json, ref int index, ref bool success, ref int line)1008 protected static object ParseNumber(char[] json, ref int index, ref bool success, ref int line) 1009 { 1010 EatWhitespace(json, ref index, ref line); 1011 EatComment(json, ref index, ref line); 1012 1013 int lastIndex = GetLastIndexOfNumber(json, index); 1014 int charLength = (lastIndex - index) + 1; 1015 1016 object returnNumber; 1017 string str = new string(json, index, charLength); 1018 if(str.StartsWith("0x")) 1019 { 1020 long number; 1021 1022 success = long.TryParse(str.Substring(2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out number); 1023 if(number <= int.MaxValue && number >= int.MinValue) 1024 { 1025 returnNumber = (int)number; 1026 } 1027 else 1028 { 1029 returnNumber = number; 1030 } 1031 } 1032 else 1033 if(str.IndexOfAny(HexArray) != -1) 1034 { 1035 success = false; 1036 returnNumber = 0; 1037 } 1038 else 1039 { 1040 if(str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) 1041 { 1042 double number; 1043 success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); 1044 returnNumber = number; 1045 } 1046 else 1047 { 1048 long number; 1049 success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); 1050 if(number <= int.MaxValue && number >= int.MinValue) 1051 { 1052 returnNumber = (int)number; 1053 } 1054 else 1055 { 1056 returnNumber = number; 1057 } 1058 } 1059 } 1060 index = lastIndex + 1; 1061 return returnNumber; 1062 } 1063 GetLastIndexOfNumber(char[] json, int index)1064 protected static int GetLastIndexOfNumber(char[] json, int index) 1065 { 1066 int lastIndex; 1067 1068 for(lastIndex = index; lastIndex < json.Length; lastIndex++) 1069 { 1070 if("0123456789ABCDEFabcdefxX+-.eE".IndexOf(json[lastIndex]) == -1) 1071 { 1072 break; 1073 } 1074 } 1075 return lastIndex - 1; 1076 } 1077 EatWhitespace(char[] json, ref int index, ref int line)1078 protected static void EatWhitespace(char[] json, ref int index, ref int line) 1079 { 1080 for(; index < json.Length; index++) 1081 { 1082 if(" \t\n\r\b\f".IndexOf(json[index]) == -1) 1083 { 1084 break; 1085 } 1086 else if(json[index] == '\n') 1087 { 1088 line++; 1089 } 1090 } 1091 } 1092 EatComment(char[] json, ref int index, ref int line)1093 protected static void EatComment(char[] json, ref int index, ref int line) 1094 { 1095 while(index < json.Length && json[index] == '/' && json[index + 1] == '*') 1096 { 1097 index += 2; 1098 while(index < json.Length - 1 && !(json[index] == '*' && json[index + 1] == '/')) 1099 { 1100 if(json[index] == '\n') 1101 { 1102 line++; 1103 } 1104 index++; 1105 } 1106 index += 2; 1107 EatWhitespace(json, ref index, ref line); 1108 } 1109 } 1110 LookAhead(char[] json, int index, int line)1111 protected static int LookAhead (char[] json, int index, int line) 1112 { 1113 int saveIndex = index; 1114 int saveLine = line; 1115 return NextToken(json, ref saveIndex, ref saveLine); 1116 } 1117 NextToken(char[] json, ref int index, ref int line)1118 protected static int NextToken(char[] json, ref int index, ref int line) 1119 { 1120 EatWhitespace(json, ref index, ref line); 1121 EatComment(json, ref index, ref line); 1122 1123 if(index >= json.Length) 1124 { 1125 return TOKEN_NONE; 1126 } 1127 1128 char c = json[index]; 1129 index++; 1130 switch(c) 1131 { 1132 case '{': 1133 return TOKEN_CURLY_OPEN; 1134 case '}': 1135 return TOKEN_CURLY_CLOSE; 1136 case '[': 1137 return TOKEN_SQUARED_OPEN; 1138 case ']': 1139 return TOKEN_SQUARED_CLOSE; 1140 case ',': 1141 return TOKEN_COMMA; 1142 case '"': 1143 return TOKEN_STRING; 1144 case '0': 1145 case '1': 1146 case '2': 1147 case '3': 1148 case '4': 1149 case '5': 1150 case '6': 1151 case '7': 1152 case '8': 1153 case '9': 1154 case '-': 1155 return TOKEN_NUMBER; 1156 case ':': 1157 return TOKEN_COLON; 1158 } 1159 index--; 1160 1161 int remainingLength = json.Length - index; 1162 1163 // false 1164 if(remainingLength >= 5) 1165 { 1166 if(json[index] == 'f' && 1167 json[index + 1] == 'a' && 1168 json[index + 2] == 'l' && 1169 json[index + 3] == 's' && 1170 json[index + 4] == 'e') 1171 { 1172 index += 5; 1173 return TOKEN_FALSE; 1174 } 1175 } 1176 1177 // true 1178 if(remainingLength >= 4) 1179 { 1180 if(json[index] == 't' && 1181 json[index + 1] == 'r' && 1182 json[index + 2] == 'u' && 1183 json[index + 3] == 'e') 1184 { 1185 index += 4; 1186 return TOKEN_TRUE; 1187 } 1188 } 1189 1190 // null 1191 if(remainingLength >= 4) 1192 { 1193 if(json[index] == 'n' && 1194 json[index + 1] == 'u' && 1195 json[index + 2] == 'l' && 1196 json[index + 3] == 'l') 1197 { 1198 index += 4; 1199 return TOKEN_NULL; 1200 } 1201 } 1202 1203 return TOKEN_NONE; 1204 } 1205 SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder)1206 protected static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) 1207 { 1208 bool success = true; 1209 1210 if(value is string) 1211 { 1212 success = SerializeString((string)value, builder); 1213 } 1214 else 1215 if(value is IDictionary<string, object>) 1216 { 1217 IDictionary<string, object> dict = (IDictionary<string, object>)value; 1218 success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); 1219 } 1220 else 1221 if(value is IDictionary<string, string>) 1222 { 1223 IDictionary<string, string> dict = (IDictionary<string, string>)value; 1224 success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); 1225 } 1226 else 1227 if(value is IEnumerable) 1228 { 1229 success = SerializeArray(jsonSerializerStrategy, (IEnumerable)value, builder); 1230 } 1231 else 1232 if(IsNumeric(value)) 1233 { 1234 success = SerializeNumber(value, builder); 1235 } 1236 else 1237 if(value is Boolean) 1238 { 1239 builder.Append((bool)value ? "true" : "false"); 1240 } 1241 else 1242 if(value == null) 1243 { 1244 builder.Append("null"); 1245 } 1246 else 1247 { 1248 object serializedObject; 1249 success = jsonSerializerStrategy.SerializeNonPrimitiveObject(value, out serializedObject); 1250 if(success) 1251 { 1252 SerializeValue(jsonSerializerStrategy, serializedObject, builder); 1253 } 1254 } 1255 1256 return success; 1257 } 1258 SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder)1259 protected static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) 1260 { 1261 builder.Append("{"); 1262 1263 IEnumerator ke = keys.GetEnumerator(); 1264 IEnumerator ve = values.GetEnumerator(); 1265 1266 bool first = true; 1267 while(ke.MoveNext() && ve.MoveNext()) 1268 { 1269 object key = ke.Current; 1270 object value = ve.Current; 1271 1272 if(!first) 1273 { 1274 builder.Append(","); 1275 } 1276 1277 if(key is string) 1278 { 1279 SerializeString((string)key, builder); 1280 } 1281 else 1282 if(!SerializeValue(jsonSerializerStrategy, value, builder)) 1283 { 1284 return false; 1285 } 1286 1287 builder.Append(":"); 1288 if(!SerializeValue(jsonSerializerStrategy, value, builder)) 1289 { 1290 return false; 1291 } 1292 1293 first = false; 1294 } 1295 1296 builder.Append("}"); 1297 return true; 1298 } 1299 SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder)1300 protected static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) 1301 { 1302 builder.Append("["); 1303 1304 bool first = true; 1305 foreach(object value in anArray) 1306 { 1307 if(!first) 1308 { 1309 builder.Append(","); 1310 } 1311 1312 if(!SerializeValue(jsonSerializerStrategy, value, builder)) 1313 { 1314 return false; 1315 } 1316 1317 first = false; 1318 } 1319 1320 builder.Append("]"); 1321 return true; 1322 } 1323 SerializeString(string aString, StringBuilder builder)1324 protected static bool SerializeString(string aString, StringBuilder builder) 1325 { 1326 builder.Append("\""); 1327 1328 char[] charArray = aString.ToCharArray(); 1329 for(int i = 0; i < charArray.Length; i++) 1330 { 1331 char c = charArray[i]; 1332 if(c == '"') 1333 { 1334 builder.Append("\\\""); 1335 } 1336 else 1337 if(c == '\\') 1338 { 1339 builder.Append("\\\\"); 1340 } 1341 else 1342 if(c == '\b') 1343 { 1344 builder.Append("\\b"); 1345 } 1346 else 1347 if(c == '\f') 1348 { 1349 builder.Append("\\f"); 1350 } 1351 else 1352 if(c == '\n') 1353 { 1354 builder.Append("\\n"); 1355 } 1356 else 1357 if(c == '\r') 1358 { 1359 builder.Append("\\r"); 1360 } 1361 else 1362 if(c == '\t') 1363 { 1364 builder.Append("\\t"); 1365 } 1366 else 1367 { 1368 builder.Append(c); 1369 } 1370 } 1371 1372 builder.Append("\""); 1373 return true; 1374 } 1375 SerializeNumber(object number, StringBuilder builder)1376 protected static bool SerializeNumber(object number, StringBuilder builder) 1377 { 1378 if(number is long) 1379 { 1380 builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); 1381 } 1382 else 1383 if(number is ulong) 1384 { 1385 builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); 1386 } 1387 else 1388 if(number is int) 1389 { 1390 builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); 1391 } 1392 else 1393 if(number is uint) 1394 { 1395 builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); 1396 } 1397 else 1398 if(number is decimal) 1399 { 1400 builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); 1401 } 1402 else 1403 if(number is float) 1404 { 1405 builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); 1406 } 1407 else 1408 { 1409 builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); 1410 } 1411 1412 return true; 1413 } 1414 1415 /// <summary> 1416 /// Determines if a given object is numeric in any way 1417 /// (can be integer, double, null, etc). 1418 /// </summary> IsNumeric(object value)1419 protected static bool IsNumeric(object value) 1420 { 1421 if(value is sbyte) 1422 { 1423 return true; 1424 } 1425 if(value is byte) 1426 { 1427 return true; 1428 } 1429 if(value is short) 1430 { 1431 return true; 1432 } 1433 if(value is ushort) 1434 { 1435 return true; 1436 } 1437 if(value is int) 1438 { 1439 return true; 1440 } 1441 if(value is uint) 1442 { 1443 return true; 1444 } 1445 if(value is long) 1446 { 1447 return true; 1448 } 1449 if(value is ulong) 1450 { 1451 return true; 1452 } 1453 if(value is float) 1454 { 1455 return true; 1456 } 1457 if(value is double) 1458 { 1459 return true; 1460 } 1461 if(value is decimal) 1462 { 1463 return true; 1464 } 1465 return false; 1466 } 1467 1468 private static IJsonSerializerStrategy currentJsonSerializerStrategy; 1469 1470 public static IJsonSerializerStrategy CurrentJsonSerializerStrategy 1471 { 1472 get 1473 { 1474 // todo: implement locking mechanism. 1475 return currentJsonSerializerStrategy ?? (currentJsonSerializerStrategy = PocoJsonSerializerStrategy); 1476 } 1477 1478 set 1479 { 1480 currentJsonSerializerStrategy = value; 1481 } 1482 } 1483 1484 private static PocoJsonSerializerStrategy pocoJsonSerializerStrategy; 1485 1486 [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] 1487 public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy 1488 { 1489 get 1490 { 1491 // todo: implement locking mechanism. 1492 return pocoJsonSerializerStrategy ?? (pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); 1493 } 1494 } 1495 } 1496 1497 #endregion 1498 1499 #region Simple Json Serializer Strategies 1500 1501 public interface IJsonSerializerStrategy 1502 { SerializeNonPrimitiveObject(object input, out object output)1503 bool SerializeNonPrimitiveObject(object input, out object output); 1504 DeserializeObject(object value, Type type)1505 object DeserializeObject(object value, Type type); 1506 } 1507 1508 public class PocoJsonSerializerStrategy : IJsonSerializerStrategy 1509 { 1510 internal CacheResolver CacheResolver; 1511 private static readonly string[] Iso8601Format = new string[] 1512 { 1513 @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", 1514 @"yyyy-MM-dd\THH:mm:ss\Z", 1515 @"yyyy-MM-dd\THH:mm:ssK" 1516 }; 1517 PocoJsonSerializerStrategy()1518 public PocoJsonSerializerStrategy() 1519 { 1520 CacheResolver = new CacheResolver(BuildMap); 1521 } 1522 BuildMap(Type type, SafeDictionary<string, CacheResolver.MemberMap> memberMaps)1523 protected virtual void BuildMap(Type type, SafeDictionary<string, CacheResolver.MemberMap> memberMaps) 1524 { 1525 foreach(PropertyInfo info in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) 1526 { 1527 memberMaps.Add(info.Name, new CacheResolver.MemberMap(info)); 1528 } 1529 foreach(FieldInfo info in type.GetFields(BindingFlags.Public | BindingFlags.Instance)) 1530 { 1531 memberMaps.Add(info.Name, new CacheResolver.MemberMap(info)); 1532 } 1533 } 1534 SerializeNonPrimitiveObject(object input, out object output)1535 public virtual bool SerializeNonPrimitiveObject(object input, out object output) 1536 { 1537 return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); 1538 } 1539 DeserializeObject(object value, Type type)1540 public virtual object DeserializeObject(object value, Type type) 1541 { 1542 object obj = null; 1543 if(value is string) 1544 { 1545 string str = value as string; 1546 if(!string.IsNullOrEmpty(str) && (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime)))) 1547 { 1548 obj = DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); 1549 } 1550 else 1551 { 1552 obj = str; 1553 } 1554 } 1555 else 1556 if(value is bool) 1557 { 1558 obj = value; 1559 } 1560 else 1561 if(value == null) 1562 { 1563 obj = null; 1564 } 1565 else 1566 if((value is long && type == typeof(long)) || (value is double && type == typeof(double)) || (value is int && type == typeof(int))) 1567 { 1568 obj = value; 1569 } 1570 else 1571 if((value is double && type != typeof(double)) || (value is long && type != typeof(long)) || (value is int && type != typeof(int))) 1572 { 1573 obj = typeof(IConvertible).IsAssignableFrom(type) ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) : value; 1574 } 1575 else 1576 { 1577 if(value is IDictionary<string, object>) 1578 { 1579 IDictionary<string, object> jsonObject = (IDictionary<string, object>)value; 1580 1581 if(ReflectionUtils.IsTypeDictionary(type)) 1582 { 1583 // if dictionary then 1584 Type keyType = type.GetGenericArguments()[0]; 1585 Type valueType = type.GetGenericArguments()[1]; 1586 1587 Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); 1588 IDictionary dict = (IDictionary)CacheResolver.GetNewInstance(genericType); 1589 foreach(KeyValuePair<string, object> kvp in jsonObject) 1590 { 1591 dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); 1592 } 1593 1594 obj = dict; 1595 } 1596 else 1597 { 1598 obj = CacheResolver.GetNewInstance(type); 1599 SafeDictionary<string, CacheResolver.MemberMap> maps = CacheResolver.LoadMaps(type); 1600 1601 if(maps == null) 1602 { 1603 obj = value; 1604 } 1605 else 1606 { 1607 foreach(KeyValuePair<string, CacheResolver.MemberMap> keyValuePair in maps) 1608 { 1609 CacheResolver.MemberMap v = keyValuePair.Value; 1610 if(v.Setter == null) 1611 { 1612 continue; 1613 } 1614 1615 string jsonKey = keyValuePair.Key; 1616 if(jsonObject.ContainsKey(jsonKey)) 1617 { 1618 object jsonValue = DeserializeObject(jsonObject[jsonKey], v.Type); 1619 v.Setter(obj, jsonValue); 1620 } 1621 } 1622 } 1623 } 1624 } 1625 else 1626 if(value is IList<object>) 1627 { 1628 IList<object> jsonObject = (IList<object>)value; 1629 IList list = null; 1630 1631 if(type.IsArray) 1632 { 1633 list = (IList)Activator.CreateInstance(type, jsonObject.Count); 1634 int i = 0; 1635 foreach(object o in jsonObject) 1636 list[i++] = DeserializeObject(o, type.GetElementType()); 1637 } 1638 else 1639 if(ReflectionUtils.IsTypeGenericeCollectionInterface(type) || typeof(IList).IsAssignableFrom(type)) 1640 { 1641 Type innerType = type.GetGenericArguments()[0]; 1642 Type genericType = typeof(List<>).MakeGenericType(innerType); 1643 list = (IList)CacheResolver.GetNewInstance(genericType); 1644 foreach(object o in jsonObject) 1645 list.Add(DeserializeObject(o, innerType)); 1646 } 1647 1648 obj = list; 1649 } 1650 } 1651 1652 if(obj == null && value != null) 1653 { 1654 Logging.Logger.Log(Logging.LogLevel.Warning, "SimpleJson: Failed to properly deserialize value '{0}' of type '{1}'!", value, value.GetType()); 1655 } 1656 1657 if(ReflectionUtils.IsNullableType(type)) 1658 { 1659 return ReflectionUtils.ToNullableType(obj, type); 1660 } 1661 1662 return obj; 1663 } 1664 SerializeEnum(Enum p)1665 protected virtual object SerializeEnum(Enum p) 1666 { 1667 return Convert.ToDouble(p, CultureInfo.InvariantCulture); 1668 } 1669 TrySerializeKnownTypes(object input, out object output)1670 protected virtual bool TrySerializeKnownTypes(object input, out object output) 1671 { 1672 bool returnValue = true; 1673 if(input is DateTime) 1674 { 1675 output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); 1676 } 1677 else 1678 if(input is Guid) 1679 { 1680 output = ((Guid)input).ToString("D"); 1681 } 1682 else 1683 if(input is Uri) 1684 { 1685 output = input.ToString(); 1686 } 1687 else 1688 if(input is Enum) 1689 { 1690 output = SerializeEnum((Enum)input); 1691 } 1692 else 1693 { 1694 returnValue = false; 1695 output = null; 1696 } 1697 1698 return returnValue; 1699 } 1700 TrySerializeUnknownTypes(object input, out object output)1701 protected virtual bool TrySerializeUnknownTypes(object input, out object output) 1702 { 1703 output = null; 1704 1705 // todo: implement caching for types 1706 Type type = input.GetType(); 1707 1708 if(type.FullName == null) 1709 { 1710 return false; 1711 } 1712 1713 IDictionary<string, object> obj = new JsonObject(); 1714 1715 SafeDictionary<string, CacheResolver.MemberMap> maps = CacheResolver.LoadMaps(type); 1716 1717 foreach(KeyValuePair<string, CacheResolver.MemberMap> keyValuePair in maps) 1718 { 1719 if(keyValuePair.Value.Getter != null) 1720 { 1721 obj.Add(keyValuePair.Key, keyValuePair.Value.Getter(input)); 1722 } 1723 } 1724 1725 output = obj; 1726 return true; 1727 } 1728 } 1729 #endregion 1730 1731 #region Reflection helpers 1732 1733 namespace Reflection 1734 { 1735 internal class ReflectionUtils 1736 { GetAttribute(MemberInfo info, Type type)1737 public static Attribute GetAttribute(MemberInfo info, Type type) 1738 { 1739 if(info == null || type == null || !Attribute.IsDefined(info, type)) 1740 { 1741 return null; 1742 } 1743 return Attribute.GetCustomAttribute(info, type); 1744 } 1745 GetAttribute(Type objectType, Type attributeType)1746 public static Attribute GetAttribute(Type objectType, Type attributeType) 1747 { 1748 1749 if(objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) 1750 { 1751 return null; 1752 } 1753 return Attribute.GetCustomAttribute(objectType, attributeType); 1754 } 1755 IsTypeGenericeCollectionInterface(Type type)1756 public static bool IsTypeGenericeCollectionInterface(Type type) 1757 { 1758 if(!type.IsGenericType) 1759 { 1760 return false; 1761 } 1762 1763 Type genericDefinition = type.GetGenericTypeDefinition(); 1764 1765 return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>)); 1766 } 1767 IsTypeDictionary(Type type)1768 public static bool IsTypeDictionary(Type type) 1769 { 1770 if(typeof(IDictionary).IsAssignableFrom(type)) 1771 { 1772 return true; 1773 } 1774 1775 if(!type.IsGenericType) 1776 { 1777 return false; 1778 } 1779 Type genericDefinition = type.GetGenericTypeDefinition(); 1780 return genericDefinition == typeof(IDictionary<,>); 1781 } 1782 IsNullableType(Type type)1783 public static bool IsNullableType(Type type) 1784 { 1785 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 1786 } 1787 ToNullableType(object obj, Type nullableType)1788 public static object ToNullableType(object obj, Type nullableType) 1789 { 1790 return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); 1791 } 1792 } 1793 GetHandler(object source)1794 public delegate object GetHandler(object source); 1795 SetHandler(object source,object value)1796 public delegate void SetHandler(object source,object value); 1797 MemberMapLoader(Type type,SafeDictionary<string, CacheResolver.MemberMap> memberMaps)1798 public delegate void MemberMapLoader(Type type,SafeDictionary<string, CacheResolver.MemberMap> memberMaps); 1799 1800 public class CacheResolver 1801 { 1802 private readonly MemberMapLoader _memberMapLoader; 1803 private readonly SafeDictionary<Type, SafeDictionary<string, MemberMap>> _memberMapsCache = new SafeDictionary<Type, SafeDictionary<string, MemberMap>>(); 1804 CtorDelegate()1805 delegate object CtorDelegate(); 1806 1807 readonly static SafeDictionary<Type, CtorDelegate> ConstructorCache = new SafeDictionary<Type, CtorDelegate>(); 1808 CacheResolver(MemberMapLoader memberMapLoader)1809 public CacheResolver(MemberMapLoader memberMapLoader) 1810 { 1811 _memberMapLoader = memberMapLoader; 1812 } 1813 1814 [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] GetNewInstance(Type type)1815 public static object GetNewInstance(Type type) 1816 { 1817 CtorDelegate c; 1818 if(ConstructorCache.TryGetValue(type, out c)) 1819 { 1820 return c(); 1821 } 1822 1823 ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); 1824 1825 c = delegate 1826 { 1827 return constructorInfo.Invoke(null); 1828 }; 1829 ConstructorCache.Add(type, c); 1830 return c(); 1831 } 1832 LoadMaps(Type type)1833 public SafeDictionary<string, MemberMap> LoadMaps(Type type) 1834 { 1835 if(type == null || type == typeof(object)) 1836 { 1837 return null; 1838 } 1839 SafeDictionary<string, MemberMap> maps; 1840 if(_memberMapsCache.TryGetValue(type, out maps)) 1841 { 1842 return maps; 1843 } 1844 maps = new SafeDictionary<string, MemberMap>(); 1845 _memberMapLoader(type, maps); 1846 _memberMapsCache.Add(type, maps); 1847 return maps; 1848 } 1849 CreateGetHandler(FieldInfo fieldInfo)1850 static GetHandler CreateGetHandler(FieldInfo fieldInfo) 1851 { 1852 return delegate(object instance) 1853 { 1854 return fieldInfo.GetValue(instance); 1855 }; 1856 } 1857 CreateSetHandler(FieldInfo fieldInfo)1858 static SetHandler CreateSetHandler(FieldInfo fieldInfo) 1859 { 1860 if(fieldInfo.IsInitOnly || fieldInfo.IsLiteral) 1861 { 1862 return null; 1863 } 1864 return delegate(object instance, object value) 1865 { 1866 fieldInfo.SetValue(instance, value); 1867 }; 1868 } 1869 CreateGetHandler(PropertyInfo propertyInfo)1870 static GetHandler CreateGetHandler(PropertyInfo propertyInfo) 1871 { 1872 MethodInfo getMethodInfo = propertyInfo.GetGetMethod(true); 1873 if(getMethodInfo == null) 1874 { 1875 return null; 1876 } 1877 1878 return delegate(object instance) 1879 { 1880 return getMethodInfo.Invoke(instance, Type.EmptyTypes); 1881 }; 1882 } 1883 CreateSetHandler(PropertyInfo propertyInfo)1884 static SetHandler CreateSetHandler(PropertyInfo propertyInfo) 1885 { 1886 MethodInfo setMethodInfo = propertyInfo.GetSetMethod(true); 1887 if(setMethodInfo == null) 1888 { 1889 return null; 1890 } 1891 return delegate(object instance, object value) 1892 { 1893 setMethodInfo.Invoke(instance, new[] { value }); 1894 }; 1895 } 1896 1897 public sealed class MemberMap 1898 { 1899 public readonly MemberInfo MemberInfo; 1900 public readonly Type Type; 1901 public readonly GetHandler Getter; 1902 public readonly SetHandler Setter; 1903 MemberMap(PropertyInfo propertyInfo)1904 public MemberMap(PropertyInfo propertyInfo) 1905 { 1906 MemberInfo = propertyInfo; 1907 Type = propertyInfo.PropertyType; 1908 Getter = CreateGetHandler(propertyInfo); 1909 Setter = CreateSetHandler(propertyInfo); 1910 } 1911 MemberMap(FieldInfo fieldInfo)1912 public MemberMap(FieldInfo fieldInfo) 1913 { 1914 MemberInfo = fieldInfo; 1915 Type = fieldInfo.FieldType; 1916 Getter = CreateGetHandler(fieldInfo); 1917 Setter = CreateSetHandler(fieldInfo); 1918 } 1919 } 1920 } 1921 1922 public class SafeDictionary<TKey, TValue> 1923 { 1924 private readonly object _padlock = new object(); 1925 private readonly Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>(); 1926 TryGetValue(TKey key, out TValue value)1927 public bool TryGetValue(TKey key, out TValue value) 1928 { 1929 return _dictionary.TryGetValue(key, out value); 1930 } 1931 1932 public TValue this[TKey key] 1933 { 1934 get { return _dictionary[key]; } 1935 } 1936 GetEnumerator()1937 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() 1938 { 1939 return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator(); 1940 } 1941 Add(TKey key, TValue value)1942 public void Add(TKey key, TValue value) 1943 { 1944 lock(_padlock) 1945 { 1946 if(_dictionary.ContainsKey(key) == false) 1947 { 1948 _dictionary.Add(key, value); 1949 } 1950 } 1951 } 1952 } 1953 } 1954 1955 #endregion 1956 } 1957