Statistics
| Branch: | Revision:

root / trunk / hammock / src / net35 / Hammock / Serialization / JsonParser.cs @ 0eea575a

History | View | Annotate | Download (29.7 kB)

1
#region Public Domain License
2
/*
3
 * JSON Parser
4
 * Written by Daniel Crenna
5
 * (http://danielcrenna.com)
6
 * 
7
 * This work is public domain.
8
 * 
9
 * "The person who associated a work with this document has 
10
 * dedicated the work to the Commons by waiving all of his
11
 * or her rights to the work worldwide under copyright law
12
 * and all related or neighboring legal rights he or she
13
 * had in the work, to the extent allowable by law."
14
 * 
15
 * For more information, please visit:
16
 * http://creativecommons.org/publicdomain/zero/1.0/
17
 */
18
#endregion
19

    
20
using System;
21
using System.Collections;
22
using System.Collections.Generic;
23
using System.Globalization;
24
using System.Linq;
25
using System.Reflection;
26
using System.Text;
27
#if NET40
28
using System.Dynamic;
29
#endif
30
#if NETCF
31
using Hammock.Extensions;
32
#endif
33

    
34

    
35
namespace Hammock.Serialization
36
{
37
    /// <summary>
38
    /// Possible JSON tokens in parsed input.
39
    /// </summary>
40
    public enum JsonToken
41
    {
42
        Unknown,
43
        LeftBrace,
44
        RightBrace,
45
        Colon,
46
        Comma,
47
        LeftBracket,
48
        RightBracket,
49
        String,
50
        Number,
51
        True,
52
        False,
53
        Null
54
    }
55

    
56
    /// <summary>
57
    /// Exception raised when <see cref="JsonParser" /> encounters an invalid token.
58
    /// </summary>
59
    public class InvalidJsonException : Exception
60
    {
61
        public InvalidJsonException(string message)
62
            : base(message)
63
        {
64

    
65
        }
66
    }
67

    
68
#if NET40
69
    public interface IJson { }
70

    
71
    public class JsonArray : DynamicObject, IEnumerable, IJson
72
    {
73
        private readonly List<IJson> _collection;
74

    
75
        public JsonArray(ICollection<object> collection)
76
        {
77
            _collection = new List<IJson>(collection.Count);
78
            foreach (var instance in collection.Cast<IDictionary<string, object>>())
79
            {
80
                _collection.Add(new JsonObject(instance));
81
            }
82
        }
83

    
84
        public IEnumerator GetEnumerator()
85
        {
86
            return _collection.GetEnumerator();
87
        }
88
    }
89

    
90
    public class JsonObject : DynamicObject, IJson
91
    {
92
        private readonly IDictionary<string, object> _hash = new Dictionary<string, object>();
93

    
94
        public JsonObject(IDictionary<string, object> hash)
95
        {
96
            _hash = hash;
97
        }
98

    
99
        public override bool TrySetMember(SetMemberBinder binder, object value)
100
        {
101
            var name = Underscored(binder.Name);
102
            _hash[name] = value;
103
            return _hash[name] == value;
104
        }
105

    
106
        public override bool TryGetMember(GetMemberBinder binder, out object result)
107
        {
108
            var name = Underscored(binder.Name);
109
            return YieldMember(name, out result);
110
        }
111

    
112
        private bool YieldMember(string name, out object result)
113
        {
114
            if (_hash.ContainsKey(name))
115
            {
116
                result = _hash[name];
117

    
118
                if (result is IDictionary<string, object>)
119
                {
120
                    result = new JsonObject((IDictionary<string, object>)result);
121
                    return true;
122
                }
123

    
124
                return _hash[name] == result;
125
            }
126
            result = null;
127
            return false;
128
        }
129

    
130
        private static string Underscored(IEnumerable<char> pascalCase)
131
        {
132
            var sb = new StringBuilder();
133
            var i = 0;
134
            foreach (var c in pascalCase)
135
            {
136
                if (char.IsUpper(c) && i > 0)
137
                {
138
                    sb.Append("_");
139
                }
140
                sb.Append(c);
141
                i++;
142
            }
143
            return sb.ToString().ToLowerInvariant();
144
        }
145
    }
146
#endif
147

    
148
    /// <summary>
149
    /// A parser for JSON.
150
    /// <seealso cref="http://json.org" />
151
    /// </summary>
152
    public class JsonParser
153
    {
154
#if !NETCF
155
        private const NumberStyles JsonNumbers = NumberStyles.Float;
156
#endif
157
        private static readonly IDictionary<Type, PropertyInfo[]> _cache;
158

    
159
        private static readonly char[] _base16 = new[]
160
                             {
161
                                 '0', '1', '2', '3', 
162
                                 '4', '5', '6', '7', 
163
                                 '8', '9', 'A', 'B', 
164
                                 'C', 'D', 'E', 'F'
165
                             };
166

    
167
        static JsonParser()
168
        {
169
            _cache = new Dictionary<Type, PropertyInfo[]>(0);
170
        }
171

    
172
        public static string Serialize<T>(T instance)
173
        {
174
            var bag = GetBagForObject(instance);
175

    
176
            return ToJson(bag);
177
        }
178

    
179
        public static object Deserialize(string json, Type type)
180
        {
181
            object instance;
182
            var map = PrepareInstance(out instance, type);
183
            var bag = FromJson(json);
184

    
185
            DeserializeImpl(map, bag, instance);
186
            return instance;
187
        }
188

    
189
        public static T Deserialize<T>(string json)
190
        {
191
            T instance;
192
            var map = PrepareInstance(out instance);
193
            var bag = FromJson(json);
194

    
195
            DeserializeImpl(map, bag, instance);
196
            return instance;
197
        }
198

    
199
#if NET40
200
        public static dynamic Deserialize(string json)
201
        {
202
            JsonToken type;
203
            var inner = FromJson(json, out type);
204
            dynamic instance = null;
205

    
206
            switch (type)
207
            {
208
                case JsonToken.LeftBrace:
209
                    var @object = (IDictionary<string, object>)inner.Single().Value;
210
                    instance = new JsonObject(@object);
211
                    break;
212
                case JsonToken.LeftBracket:
213
                    var @array = (IList<object>)inner.Single().Value;
214
                    instance = new JsonArray(@array);
215
                    break;
216
            }
217

    
218
            return instance;
219
        }
220
#endif
221

    
222
        private static void DeserializeImpl(IEnumerable<PropertyInfo> map,
223
                                            IDictionary<string, object> bag,
224
                                            object instance)
225
        {
226
            DeserializeType(map, bag, instance);
227
        }
228

    
229
        private static void DeserializeImpl<T>(IEnumerable<PropertyInfo> map,
230
                                               IDictionary<string, object> bag,
231
                                               T instance)
232
        {
233
            DeserializeType(map, bag, instance);
234
        }
235

    
236
        private static void DeserializeType(IEnumerable<PropertyInfo> map, IDictionary<string, object> bag, object instance)
237
        {
238
            foreach (var info in map)
239
            {
240
                var key = info.Name;
241
                if (!bag.ContainsKey(key))
242
                {
243
                    key = info.Name.Replace("_", "");
244
                    if (!bag.ContainsKey(key))
245
                    {
246
                        key = info.Name.Replace("-", "");
247
                        if (!bag.ContainsKey(key))
248
                        {
249
                            continue;
250
                        }
251
                    }
252
                }
253

    
254
                var value = bag[key];
255
                if (info.PropertyType == typeof(DateTime))
256
                {
257
                    // Dates (Not part of spec, using lossy epoch convention)
258
                    var seconds = Int32.Parse(
259
                        value.ToString(), NumberStyles.Number, CultureInfo.InvariantCulture
260
                        );
261
                    var time = new DateTime(1970, 1, 1).ToUniversalTime();
262
                    value = time.AddSeconds(seconds);
263
                }
264

    
265
                if (info.PropertyType == typeof(byte[]))
266
                {
267
                    var bytes = (List<object>)value;
268
#if NETCF
269
                    value = bytes.Select(o => Convert.ToByte(o)).ToArray();
270
#else
271
                    value = bytes.Select(Convert.ToByte).ToArray();
272
#endif
273
                }
274

    
275
                if (info.PropertyType == typeof(double))
276
                {
277
                    value = Convert.ToDouble(value);
278
                }
279

    
280
                if (info.PropertyType == typeof(int))
281
                {
282
                    value = Convert.ToInt32(value);
283
                }
284

    
285
                if (info.PropertyType == typeof(long))
286
                {
287
                    value = Convert.ToInt64(value);
288
                }
289

    
290
                info.SetValue(instance, value, null);
291
            }
292
        }
293

    
294
        public static IDictionary<string, object> FromJson(string json)
295
        {
296
            JsonToken type;
297

    
298
            var result = FromJson(json, out type);
299

    
300
            switch (type)
301
            {
302
                case JsonToken.LeftBrace:
303
                    var @object = (IDictionary<string, object>)result.Single().Value;
304
                    return @object;
305
            }
306

    
307
            return result;
308
        }
309

    
310
        public static IDictionary<string, object> FromJson(string json, out JsonToken type)
311
        {
312
            var data = json.ToCharArray();
313
            var index = 0;
314

    
315
            // Rewind index for first token
316
            var token = NextToken(data, ref index);
317
            switch (token)
318
            {
319
                case JsonToken.LeftBrace:   // Start Object
320
                case JsonToken.LeftBracket: // Start Array
321
                    index--;
322
                    type = token;
323
                    break;
324
                default:
325
                    throw new InvalidJsonException("JSON must begin with an object or array");
326
            }
327

    
328
            return ParseObject(data, ref index);
329
        }
330

    
331
        public static string ToJson(IDictionary<string, object> bag)
332
        {
333
            var sb = new StringBuilder(0);
334

    
335
            SerializeItem(sb, bag);
336

    
337
            return sb.ToString();
338
        }
339

    
340
        internal static IDictionary<string, object> GetBagForObject(Type type, object instance)
341
        {
342
            CacheReflection(type);
343

    
344
            if (type.FullName == null)
345
            {
346
                return null;
347
            }
348

    
349
            var anonymous = type.FullName.Contains("__AnonymousType");
350
            var map = _cache[type];
351

    
352
            IDictionary<string, object> bag = InitializeBag();
353
            foreach (var info in map)
354
            {
355
                var readWrite = (info.CanWrite && info.CanRead);
356
                if (!readWrite && !anonymous)
357
                {
358
                    continue;
359
                }
360
                var value = info.GetValue(instance, null);
361
                bag.Add(info.Name, value);
362
            }
363

    
364
            return bag;
365
        }
366

    
367
        internal static IDictionary<string, object> GetBagForObject<T>(T instance)
368
        {
369
            return GetBagForObject(typeof(T), instance);
370
        }
371

    
372
        internal static Dictionary<string, object> InitializeBag()
373
        {
374
            return new Dictionary<string, object>(
375
                0, StringComparer.OrdinalIgnoreCase
376
                );
377
        }
378

    
379
        internal static IEnumerable<PropertyInfo> PrepareInstance(out object instance, Type type)
380
        {
381
            instance = Activator.CreateInstance(type);
382

    
383
            CacheReflection(type);
384

    
385
            return _cache[type];
386
        }
387

    
388
        internal static IEnumerable<PropertyInfo> PrepareInstance<T>(out T instance)
389
        {
390
            instance = Activator.CreateInstance<T>();
391
            var item = typeof(T);
392

    
393
            CacheReflection(item);
394

    
395
            return _cache[item];
396
        }
397

    
398
        internal static void CacheReflection(Type item)
399
        {
400
            if (_cache.ContainsKey(item))
401
            {
402
                return;
403
            }
404

    
405
            var properties = item.GetProperties(
406
                BindingFlags.Public | BindingFlags.Instance
407
                );
408

    
409
            _cache.Add(item, properties);
410
        }
411

    
412
        internal static void SerializeItem(StringBuilder sb, object item)
413
        {
414
            if (item is IDictionary<string, object>)
415
            {
416
                SerializeObject(item, sb);
417
                return;
418
            }
419

    
420
            if (item is IEnumerable)
421
            {
422
                SerializeArray(item, sb);
423
                return;
424
            }
425

    
426
            if (item is DateTime)
427
            {
428
                SerializeDateTime(sb);
429
                return;
430
            }
431

    
432
            if (item is bool)
433
            {
434
                sb.Append(((bool)item).ToString().ToLower());
435
                return;
436
            }
437

    
438
            double number;
439
            var input = item != null ? item.ToString() : "";
440
#if NETCF
441
            if (input.TryParse(out number))
442
            {
443
              sb.Append(number);
444
            }
445
#else
446
            if (double.TryParse(input, JsonNumbers, CultureInfo.InvariantCulture, out number))
447
            {
448
                sb.Append(number);
449
                return;
450
            }
451
#endif
452
            if (item == null)
453
            {
454
                sb.Append("null");
455
                return;
456
            }
457

    
458
            var bag = GetBagForObject(item.GetType(), item);
459
            SerializeItem(sb, bag);
460
        }
461

    
462
        internal static void SerializeDateTime(StringBuilder sb)
463
        {
464
            var elapsed = DateTime.UtcNow - new DateTime(1970, 1, 1).ToUniversalTime();
465
            var epoch = (long)elapsed.TotalSeconds;
466
            SerializeString(sb, epoch);
467
        }
468

    
469
        internal static void SerializeArray(object item, StringBuilder sb)
470
        {
471
            var array = (IEnumerable)item;
472
            sb.Append("[");
473
            var count = 0;
474

    
475
            var total = array.Cast<object>().Count();
476
            foreach (var element in array)
477
            {
478
                SerializeItem(sb, element);
479
                count++;
480
                if (count < total)
481
                {
482
                    sb.Append(",");
483
                }
484
            }
485
            sb.Append("]");
486
        }
487

    
488
        internal static void SerializeObject(object item, StringBuilder sb)
489
        {
490
            var nested = (IDictionary<string, object>)item;
491
            sb.Append("{");
492

    
493
            var count = 0;
494
            foreach (var key in nested.Keys)
495
            {
496
                SerializeString(sb, key.ToLower());
497
                sb.Append(":");
498

    
499
                var value = nested[key];
500
                if (value is string)
501
                {
502
                    SerializeString(sb, value);
503
                }
504
                else
505
                {
506
                    SerializeItem(sb, nested[key]);
507
                }
508

    
509
                if (count < nested.Keys.Count - 1)
510
                {
511
                    sb.Append(",");
512
                }
513
                count++;
514
            }
515
            sb.Append("}");
516
        }
517

    
518

    
519
        internal static void SerializeString(StringBuilder sb, object item)
520
        {
521
            sb.Append("\"");
522
            var symbols = item.ToString().ToCharArray();
523
#if NETCF
524
            foreach (var unicode in symbols.Select(symbol => (int)symbol).Select(code => GetUnicode(code)))
525
#else
526
            foreach (var unicode in symbols.Select(symbol => (int)symbol).Select(GetUnicode))
527
#endif
528
            {
529
                sb.Append(unicode);
530
            }
531
            sb.Append("\"");
532
        }
533

    
534
        internal static string GetUnicode(int code)
535
        {
536
            // http://unicode.org/roadmaps/bmp/
537
            var basicLatin = code >= 32 && code <= 126;
538
            if (basicLatin)
539
            {
540
                return new string((char)code, 1);
541
            }
542

    
543
            var unicode = BaseConvert(code, _base16, 4);
544
            return string.Concat("\\u", unicode);
545
        }
546

    
547
        internal static KeyValuePair<string, object> ParsePair(IList<char> data, ref int index)
548
        {
549
            var valid = true;
550

    
551
            var name = ParseString(data, ref index);
552
            if (name == null)
553
            {
554
                valid = false;
555
            }
556

    
557
            if (!ParseToken(JsonToken.Colon, data, ref index))
558
            {
559
                valid = false;
560
            }
561

    
562
            if (!valid)
563
            {
564
                throw new InvalidJsonException(string.Format(
565
                            "Invalid JSON found while parsing a value pair at index {0}.", index
566
                            ));
567
            }
568

    
569
            index++;
570
            var value = ParseValue(data, ref index);
571
            return new KeyValuePair<string, object>(name, value);
572
        }
573

    
574
        internal static bool ParseToken(JsonToken token, IList<char> data, ref int index)
575
        {
576
            var nextToken = NextToken(data, ref index);
577
            return token == nextToken;
578
        }
579

    
580
        internal static string ParseString(IList<char> data, ref int index)
581
        {
582
            var symbol = data[index];
583
            IgnoreWhitespace(data, ref index, symbol);
584
            symbol = data[++index]; // Skip first quotation
585

    
586
            var sb = new StringBuilder();
587
            while (true)
588
            {
589
                if (index >= data.Count - 1)
590
                {
591
                    return null;
592
                }
593
                switch (symbol)
594
                {
595
                    case '"':  // End String
596
                        index++;
597
                        return sb.ToString();
598
                    case '\\': // Control Character
599
                        symbol = data[++index];
600
                        switch (symbol)
601
                        {
602
                            case '/':
603
                                sb.Append(symbol);
604
                                break;
605
                            case '\\':
606
                            case 'b':
607
                            case 'f':
608
                            case 'n':
609
                            case 'r':
610
                            case 't':
611
                                break;
612
                            case 'u': // Unicode literals
613
                                if (index < data.Count - 5)
614
                                {
615
                                    var array = data.ToArray();
616
                                    var buffer = new char[4];
617
                                    Array.Copy(array, index + 1, buffer, 0, 4);
618

    
619
                                    // http://msdn.microsoft.com/en-us/library/aa664669%28VS.71%29.aspx
620
                                    // http://www.yoda.arachsys.com/csharp/unicode.html
621
                                    // http://en.wikipedia.org/wiki/UTF-32/UCS-4
622
                                    var hex = new string(buffer);
623
                                    var unicode = (char)Convert.ToInt32(hex, 16);
624
                                    sb.Append(unicode);
625
                                    index += 4;
626
                                }
627
                                else
628
                                {
629
                                    break;
630
                                }
631
                                break;
632
                        }
633
                        break;
634
                    default:
635
                        sb.Append(symbol);
636
                        break;
637
                }
638
                symbol = data[++index];
639
            }
640
        }
641

    
642
        internal static object ParseValue(IList<char> data, ref int index)
643
        {
644
            var token = NextToken(data, ref index);
645
            
646
            switch (token)
647
            {
648
                // End Tokens
649
                case JsonToken.RightBracket:    // Bad Data
650
                case JsonToken.RightBrace:
651
                case JsonToken.Unknown:
652
                case JsonToken.Colon:
653
                case JsonToken.Comma:
654
                    throw new InvalidJsonException(string.Format(
655
                            "Invalid JSON found while parsing a value at index {0}.", index
656
                            ));
657
                // Value Tokens
658
                case JsonToken.LeftBrace:
659
                    return ParseObject(data, ref index);
660
                case JsonToken.LeftBracket:
661
                    return ParseArray(data, ref index);
662
                case JsonToken.String:
663
                    return ParseString(data, ref index);
664
                case JsonToken.Number:
665
                    return ParseNumber(data, ref index);
666
                case JsonToken.True:
667
                    return true;
668
                case JsonToken.False:
669
                    return false;
670
                case JsonToken.Null:
671
                    return null;
672
                default:
673
                    throw new ArgumentOutOfRangeException();
674
            }
675
        }
676

    
677
        internal static IDictionary<string, object> ParseObject(IList<char> data, ref int index)
678
        {
679
            var result = InitializeBag();
680

    
681
            index++; // Skip first token
682

    
683
            while (index < data.Count - 1)
684
            {
685
                var token = NextToken(data, ref index);
686
                switch (token)
687
                {
688
                    // End Tokens
689
                    case JsonToken.Unknown:             // Bad Data
690
                    case JsonToken.True:
691
                    case JsonToken.False:
692
                    case JsonToken.Null:
693
                    case JsonToken.Colon:
694
                    case JsonToken.RightBracket:
695
                    case JsonToken.Number:
696
                        throw new InvalidJsonException(string.Format(
697
                            "Invalid JSON found while parsing an object at index {0}.", index
698
                            ));
699
                    case JsonToken.RightBrace:          // End Object
700
                        index++;
701
                        return result;
702
                    // Skip Tokens
703
                    case JsonToken.Comma:
704
                        index++;
705
                        break;
706
                    // Start Tokens
707
                    case JsonToken.LeftBrace:           // Start Object
708
                        var @object = ParseObject(data, ref index);
709
                        if (@object != null)
710
                        {
711
                            result.Add(string.Concat("object", result.Count), @object);
712
                        }
713
                        index++;
714
                        break;
715
                    case JsonToken.LeftBracket:         // Start Array
716
                        var @array = ParseArray(data, ref index);
717
                        if (@array != null)
718
                        {
719
                            result.Add(string.Concat("array", result.Count), @array);
720
                        }
721
                        index++;
722
                        break;
723
                    case JsonToken.String:
724
                        var pair = ParsePair(data, ref index);
725
                        result.Add(pair.Key, pair.Value);
726
                        break;
727
                    default:
728
                        throw new NotSupportedException("Invalid token expected.");
729
                }
730
            }
731

    
732
            return result;
733
        }
734

    
735
        internal static IEnumerable<object> ParseArray(IList<char> data, ref int index)
736
        {
737
            var result = new List<object>();
738

    
739
            index++; // Skip first bracket
740
            while (index < data.Count - 1)
741
            {
742
                var token = NextToken(data, ref index);
743
                switch (token)
744
                {
745
                    // End Tokens
746
                    case JsonToken.Unknown:             // Bad Data
747
                        throw new InvalidJsonException(string.Format(
748
                            "Invalid JSON found while parsing an array at index {0}.", index
749
                            ));
750
                    case JsonToken.RightBracket:        // End Array
751
                        index++;
752
                        return result;
753
                    // Skip Tokens
754
                    case JsonToken.Comma:               // Separator
755
                    case JsonToken.RightBrace:          // End Object
756
                    case JsonToken.Colon:               // Separator
757
                        index++;
758
                        break;
759
                    // Value Tokens
760
                    case JsonToken.LeftBrace:           // Start Object
761
                        var nested = ParseObject(data, ref index);
762
                        result.Add(nested);
763
                        break;
764
                    case JsonToken.LeftBracket:         // Start Array
765
                    case JsonToken.String:
766
                    case JsonToken.Number:
767
                    case JsonToken.True:
768
                    case JsonToken.False:
769
                    case JsonToken.Null:
770
                        var value = ParseValue(data, ref index);
771
                        result.Add(value);
772
                        break;
773
                    default:
774
                        throw new ArgumentOutOfRangeException();
775
                }
776

    
777
                //index++;
778
            }
779

    
780
            return result;
781
        }
782

    
783
        internal static object ParseNumber(IList<char> data, ref int index)
784
        {
785
            var symbol = data[index];
786
            IgnoreWhitespace(data, ref index, symbol);
787

    
788
            var start = index;
789
            var length = 0;
790
            while (ParseToken(JsonToken.Number, data, ref index))
791
            {
792
                length++;
793
                index++;
794
            }
795

    
796
            var number = new char[length];
797
            Array.Copy(data.ToArray(), start, number, 0, length);
798

    
799
            double result;
800
            var buffer = new string(number);
801
#if NETCF
802
            if (!buffer.TryParse(out result))
803
            {
804
                throw new InvalidJsonException(
805
                    string.Format("Value '{0}' was not a valid JSON number", buffer)
806
                    );
807
            }
808
#else
809
            if (!double.TryParse(buffer, JsonNumbers, CultureInfo.InvariantCulture, out result))
810
            {
811
                throw new InvalidJsonException(
812
                    string.Format("Value '{0}' was not a valid JSON number", buffer)
813
                    );
814
            }
815
#endif
816

    
817
            return result;
818
        }
819

    
820
        internal static JsonToken NextToken(IList<char> data, ref int index)
821
        {
822
            var symbol = data[index];
823
            var token = GetTokenFromSymbol(symbol);
824
            token = IgnoreWhitespace(data, ref index, ref token, symbol);
825

    
826
            GetKeyword("true", JsonToken.True, data, ref index, ref token);
827
            GetKeyword("false", JsonToken.False, data, ref index, ref token);
828
            GetKeyword("null", JsonToken.Null, data, ref index, ref token);
829

    
830
            return token;
831
        }
832

    
833
        internal static JsonToken GetTokenFromSymbol(char symbol)
834
        {
835
            return GetTokenFromSymbol(symbol, JsonToken.Unknown);
836
        }
837

    
838
        internal static JsonToken GetTokenFromSymbol(char symbol, JsonToken token)
839
        {
840
            switch (symbol)
841
            {
842
                case '{':
843
                    token = JsonToken.LeftBrace;
844
                    break;
845
                case '}':
846
                    token = JsonToken.RightBrace;
847
                    break;
848
                case ':':
849
                    token = JsonToken.Colon;
850
                    break;
851
                case ',':
852
                    token = JsonToken.Comma;
853
                    break;
854
                case '[':
855
                    token = JsonToken.LeftBracket;
856
                    break;
857
                case ']':
858
                    token = JsonToken.RightBracket;
859
                    break;
860
                case '"':
861
                    token = JsonToken.String;
862
                    break;
863
                case '0':
864
                case '1':
865
                case '2':
866
                case '3':
867
                case '4':
868
                case '5':
869
                case '6':
870
                case '7':
871
                case '8':
872
                case '9':
873
                case '.':
874
                case 'e':
875
                case 'E':
876
                case '+':
877
                case '-':
878
                    token = JsonToken.Number;
879
                    break;
880
            }
881
            return token;
882
        }
883

    
884
        internal static void IgnoreWhitespace(IList<char> data, ref int index, char symbol)
885
        {
886
            var token = JsonToken.Unknown;
887
            IgnoreWhitespace(data, ref index, ref token, symbol);
888
            return;
889
        }
890

    
891
        internal static JsonToken IgnoreWhitespace(IList<char> data, ref int index, ref JsonToken token, char symbol)
892
        {
893
            switch (symbol)
894
            {
895
                case ' ':
896
                case '\\':
897
                case '/':
898
                case '\b':
899
                case '\f':
900
                case '\n':
901
                case '\r':
902
                case '\t':
903
                    index++;
904
                    token = NextToken(data, ref index);
905
                    break;
906
            }
907
            return token;
908
        }
909

    
910
        internal static void GetKeyword(string word,
911
                                       JsonToken target,
912
                                       IList<char> data,
913
                                       ref int index,
914
                                       ref JsonToken result)
915
        {
916
            var buffer = data.Count - index;
917
            if (buffer < word.Length)
918
            {
919
                return;
920
            }
921

    
922
            for (var i = 0; i < word.Length; i++)
923
            {
924
                if (data[index + i] != word[i])
925
                {
926
                    return;
927
                }
928
            }
929

    
930
            result = target;
931
            index += word.Length;
932
        }
933

    
934
        internal static string BaseConvert(int input, char[] charSet, int minLength)
935
        {
936
            var sb = new StringBuilder();
937
            var @base = charSet.Length;
938

    
939
            while (input > 0)
940
            {
941
                var index = input % @base;
942
                sb.Insert(0, new[] { charSet[index] });
943
                input = input / @base;
944
            }
945

    
946
            while (sb.Length < minLength)
947
            {
948
                sb.Insert(0, "0");
949
            }
950

    
951
            return sb.ToString();
952
        }
953
    }
954

    
955
#if NETCF
956
    public static class CompactExtensions
957
    {
958
        private const NumberStyles JsonNumbers = NumberStyles.Float;
959

    
960
        public static bool TryParse(this string input, out double result)
961
        {
962
            try
963
            {
964
                result = double.Parse(input, JsonNumbers, CultureInfo.InvariantCulture);
965
                return true;
966
            }
967
            catch (Exception)
968
            {
969
                result = 0;
970
                return false;
971
            }
972
        }
973
    }
974
#endif
975
}
976