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&lt;object>) and JsonObject(IDictionary&lt;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&lt;object>, a IDictionary&lt;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&lt;string,object> / IList&lt;object> object into a JSON string
643         /// </summary>
644         /// <param name="json">A IDictionary&lt;string,object> / IList&lt;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