Merge pull request #487 from mayerwin/patch-1
[mono.git] / mcs / class / System.Json.Microsoft / System.Json / JsonPrimitive.cs
1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2
3 using System.Collections.Generic;
4 using System.Diagnostics.CodeAnalysis;
5 using System.Diagnostics.Contracts;
6 using System.Globalization;
7 using System.Runtime.Serialization;
8 using System.Text;
9 using System.Xml;
10
11 namespace System.Json
12 {
13     /// <summary>
14     /// Represents a JavaScript Object Notation (JSON) primitive type in the common language runtime (CLR).
15     /// </summary>
16     [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix",
17         Justification = "JsonPrimitive does not represent a collection.")]
18     [DataContract]
19     public sealed class JsonPrimitive : JsonValue
20     {
21         internal const string DateTimeIsoFormat = "yyyy-MM-ddTHH:mm:ss.fffK";
22         private const string UtcString = "UTC";
23         private const string GmtString = "GMT";
24         private static readonly long UnixEpochTicks = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
25         private static readonly char[] FloatingPointChars = new char[] { '.', 'e', 'E' };
26         private static readonly Type jsonPrimitiveType = typeof(JsonPrimitive);
27         private static readonly Type uriType = typeof(Uri);
28
29         private static readonly Dictionary<Type, Func<string, ConvertResult>> stringConverters = new Dictionary<Type, Func<string, ConvertResult>>
30         {
31             { typeof(bool), new Func<string, ConvertResult>(StringToBool) },
32             { typeof(byte), new Func<string, ConvertResult>(StringToByte) },
33             { typeof(char), new Func<string, ConvertResult>(StringToChar) },
34             { typeof(sbyte), new Func<string, ConvertResult>(StringToSByte) },
35             { typeof(short), new Func<string, ConvertResult>(StringToShort) },
36             { typeof(int), new Func<string, ConvertResult>(StringToInt) },
37             { typeof(long), new Func<string, ConvertResult>(StringToLong) },
38             { typeof(ushort), new Func<string, ConvertResult>(StringToUShort) },
39             { typeof(uint), new Func<string, ConvertResult>(StringToUInt) },
40             { typeof(ulong), new Func<string, ConvertResult>(StringToULong) },
41             { typeof(float), new Func<string, ConvertResult>(StringToFloat) },
42             { typeof(double), new Func<string, ConvertResult>(StringToDouble) },
43             { typeof(decimal), new Func<string, ConvertResult>(StringToDecimal) },
44             { typeof(DateTime), new Func<string, ConvertResult>(StringToDateTime) },
45             { typeof(DateTimeOffset), new Func<string, ConvertResult>(StringToDateTimeOffset) },
46             { typeof(Guid), new Func<string, ConvertResult>(StringToGuid) },
47             { typeof(Uri), new Func<string, ConvertResult>(StringToUri) },
48         };
49
50         [DataMember]
51         private object value;
52
53         [DataMember]
54         private JsonType jsonType;
55
56         /// <summary>
57         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Boolean"/> type.
58         /// </summary>
59         /// <param name="value">The <see cref="System.Boolean"/> object that initializes the new instance.</param>
60         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
61         /// When initialized with a <see cref="System.Boolean"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Boolean"/>, which can be
62         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
63         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Boolean"/>.</remarks>
64         public JsonPrimitive(bool value)
65         {
66             jsonType = JsonType.Boolean;
67             this.value = value;
68         }
69
70         /// <summary>
71         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Byte"/> type.
72         /// </summary>
73         /// <param name="value">The <see cref="System.Byte"/> object that initializes the new instance.</param>
74         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
75         /// When initialized with a <see cref="System.Byte"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
76         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
77         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Byte"/>.</remarks>
78         public JsonPrimitive(byte value)
79         {
80             jsonType = JsonType.Number;
81             this.value = value;
82         }
83
84         /// <summary>
85         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.SByte"/> type.
86         /// </summary>
87         /// <param name="value">The <see cref="System.SByte"/> object that initializes the new instance.</param>
88         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
89         /// When initialized with a <see cref="System.SByte"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
90         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
91         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.SByte"/>.</remarks>
92         [CLSCompliant(false)]
93         public JsonPrimitive(sbyte value)
94         {
95             jsonType = JsonType.Number;
96             this.value = value;
97         }
98
99         /// <summary>
100         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Decimal"/> type.
101         /// </summary>
102         /// <param name="value">The <see cref="System.Decimal"/> object that initializes the new instance.</param>
103         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
104         /// When initialized with a <see cref="System.Decimal"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
105         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
106         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Decimal"/>.</remarks>
107         public JsonPrimitive(decimal value)
108         {
109             jsonType = JsonType.Number;
110             this.value = value;
111         }
112
113         /// <summary>
114         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Int16"/> type.
115         /// </summary>
116         /// <param name="value">The <see cref="System.Int16"/> object that initializes the new instance.</param>
117         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
118         /// When initialized with a <see cref="System.Int16"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
119         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
120         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Int16"/>.</remarks>
121         public JsonPrimitive(short value)
122         {
123             jsonType = JsonType.Number;
124             this.value = value;
125         }
126
127         /// <summary>
128         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.UInt16"/> type.
129         /// </summary>
130         /// <param name="value">The <see cref="System.UInt16"/> object that initializes the new instance.</param>
131         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
132         /// When initialized with a <see cref="System.UInt16"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
133         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
134         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.UInt16"/>.</remarks>
135         [CLSCompliant(false)]
136         public JsonPrimitive(ushort value)
137         {
138             jsonType = JsonType.Number;
139             this.value = value;
140         }
141
142         /// <summary>
143         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Int32"/> type.
144         /// </summary>
145         /// <param name="value">The <see cref="System.Int32"/> object that initializes the new instance.</param>
146         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
147         /// When initialized with a <see cref="System.Int32"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
148         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
149         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Int32"/>.</remarks>
150         public JsonPrimitive(int value)
151         {
152             jsonType = JsonType.Number;
153             this.value = value;
154         }
155
156         /// <summary>
157         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.UInt32"/> type.
158         /// </summary>
159         /// <param name="value">The <see cref="System.UInt32"/> object that initializes the new instance.</param>
160         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
161         /// When initialized with a <see cref="System.UInt32"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
162         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
163         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.UInt32"/>.</remarks>
164         [CLSCompliant(false)]
165         public JsonPrimitive(uint value)
166         {
167             jsonType = JsonType.Number;
168             this.value = value;
169         }
170
171         /// <summary>
172         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Int64"/> type.
173         /// </summary>
174         /// <param name="value">The <see cref="System.Int64"/> object that initializes the new instance.</param>
175         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
176         /// When initialized with a <see cref="System.Int64"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
177         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
178         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Int64"/>.</remarks>
179         public JsonPrimitive(long value)
180         {
181             jsonType = JsonType.Number;
182             this.value = value;
183         }
184
185         /// <summary>
186         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.UInt64"/> type.
187         /// </summary>
188         /// <param name="value">The <see cref="System.UInt64"/> object that initializes the new instance.</param>
189         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
190         /// When initialized with a <see cref="System.UInt64"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
191         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
192         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.UInt64"/>.</remarks>
193         [CLSCompliant(false)]
194         public JsonPrimitive(ulong value)
195         {
196             jsonType = JsonType.Number;
197             this.value = value;
198         }
199
200         /// <summary>
201         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Single"/> type.
202         /// </summary>
203         /// <param name="value">The <see cref="System.Single"/> object that initializes the new instance.</param>
204         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
205         /// When initialized with a <see cref="System.Single"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
206         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
207         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Single"/>.</remarks>
208         public JsonPrimitive(float value)
209         {
210             jsonType = JsonType.Number;
211             this.value = value;
212         }
213
214         /// <summary>
215         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Double"/> type.
216         /// </summary>
217         /// <param name="value">The <see cref="System.Double"/> object that initializes the new instance.</param>
218         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
219         /// When initialized with a <see cref="System.Double"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.Number"/>, which can be
220         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
221         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Double"/>.</remarks>
222         public JsonPrimitive(double value)
223         {
224             jsonType = JsonType.Number;
225             this.value = value;
226         }
227
228         /// <summary>
229         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.String"/> type.
230         /// </summary>
231         /// <param name="value">The <see cref="System.String"/> object that initializes the new instance.</param>
232         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
233         /// When initialized with a <see cref="System.String"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.String"/>, which can be
234         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
235         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.String"/>.</remarks>
236         /// <exception cref="System.ArgumentNullException">value is null.</exception>
237         [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads",
238             Justification = "This operator does not intend to represent a Uri overload.")]
239         public JsonPrimitive(string value)
240         {
241             if (value == null)
242             {
243                 throw new ArgumentNullException("value");
244             }
245
246             jsonType = JsonType.String;
247             this.value = value;
248         }
249
250         /// <summary>
251         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Char"/> type.
252         /// </summary>
253         /// <param name="value">The <see cref="System.Char"/> object that initializes the new instance.</param>
254         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
255         /// When initialized with a <see cref="System.Char"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.String"/>, which can be
256         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
257         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Char"/>.</remarks>
258         public JsonPrimitive(char value)
259         {
260             jsonType = JsonType.String;
261             this.value = value;
262         }
263
264         /// <summary>
265         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.DateTime"/> type.
266         /// </summary>
267         /// <param name="value">The <see cref="System.DateTime"/> object that initializes the new instance.</param>
268         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
269         /// When initialized with a <see cref="System.DateTime"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.String"/>, which can be
270         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
271         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.DateTime"/>.</remarks>
272         public JsonPrimitive(DateTime value)
273         {
274             jsonType = JsonType.String;
275             this.value = value;
276         }
277
278         /// <summary>
279         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.DateTimeOffset"/> type.
280         /// </summary>
281         /// <param name="value">The <see cref="System.DateTimeOffset"/> object that initializes the new instance.</param>
282         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
283         /// When initialized with a <see cref="System.DateTimeOffset"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.String"/>, which can be
284         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
285         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.DateTimeOffset"/>.</remarks>
286         public JsonPrimitive(DateTimeOffset value)
287         {
288             jsonType = JsonType.String;
289             this.value = value;
290         }
291
292         /// <summary>
293         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Uri"/> type.
294         /// </summary>
295         /// <param name="value">The <see cref="System.Uri"/> object that initializes the new instance.</param>
296         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
297         /// When initialized with a <see cref="System.Uri"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.String"/>, which can be
298         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
299         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Uri"/>.</remarks>
300         /// <exception cref="System.ArgumentNullException">value is null.</exception>
301         public JsonPrimitive(Uri value)
302         {
303             if (value == null)
304             {
305                 throw new ArgumentNullException("value");
306             }
307
308             jsonType = JsonType.String;
309             this.value = value;
310         }
311
312         /// <summary>
313         /// Initializes a new instance of a <see cref="System.Json.JsonPrimitive"/> type with a <see cref="System.Guid"/> type.
314         /// </summary>
315         /// <param name="value">The <see cref="System.Guid"/> object that initializes the new instance.</param>
316         /// <remarks>A <see cref="System.Json.JsonPrimitive"/> object stores a <see cref="System.Json.JsonType"/> and the value used to initialize it.
317         /// When initialized with a <see cref="System.Guid"/> object, the <see cref="System.Json.JsonType"/> is a <see cref="F:System.Json.JsonType.String"/>, which can be
318         /// recovered using the <see cref="System.Json.JsonPrimitive.JsonType"/> property. The value used to initialize the <see cref="System.Json.JsonPrimitive"/>
319         /// object can be recovered by casting the <see cref="System.Json.JsonPrimitive"/> to <see cref="System.Guid"/>.</remarks>
320         public JsonPrimitive(Guid value)
321         {
322             jsonType = JsonType.String;
323             this.value = value;
324         }
325
326         private JsonPrimitive(object value, JsonType type)
327         {
328             jsonType = type;
329             this.value = value;
330         }
331
332         private enum ReadAsFailureKind
333         {
334             NoFailure,
335             InvalidCast,
336             InvalidDateFormat,
337             InvalidFormat,
338             InvalidUriFormat,
339             Overflow,
340         }
341
342         /// <summary>
343         /// Gets the JsonType that is associated with this <see cref="System.Json.JsonPrimitive"/> object.
344         /// </summary>
345         public override JsonType JsonType
346         {
347             get { return jsonType; }
348         }
349
350         /// <summary>
351         /// Gets the value represented by this instance.
352         /// </summary>
353         [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
354             Justification = "Value in this context clearly refers to the underlying CLR value")]
355         public object Value
356         {
357             get { return value; }
358         }
359
360         /// <summary>
361         /// Attempts to create a <see cref="JsonPrimitive"/> instance from the specified <see cref="object"/> value.
362         /// </summary>
363         /// <param name="value">The <see cref="object"/> value to create the <see cref="JsonPrimitive"/> instance.</param>
364         /// <param name="result">The resulting <see cref="JsonPrimitive"/> instance on success, null otherwise.</param>
365         /// <returns>true if the operation is successful, false otherwise.</returns>
366         public static bool TryCreate(object value, out JsonPrimitive result)
367         {
368             bool allowedType = true;
369             JsonType jsonType = default(JsonType);
370
371             if (value != null)
372             {
373                 Type type = value.GetType();
374                 switch (Type.GetTypeCode(type))
375                 {
376                     case TypeCode.Boolean:
377                         jsonType = JsonType.Boolean;
378                         break;
379                     case TypeCode.Byte:
380                     case TypeCode.SByte:
381                     case TypeCode.Decimal:
382                     case TypeCode.Double:
383                     case TypeCode.Int16:
384                     case TypeCode.Int32:
385                     case TypeCode.Int64:
386                     case TypeCode.UInt16:
387                     case TypeCode.UInt32:
388                     case TypeCode.UInt64:
389                     case TypeCode.Single:
390                         jsonType = JsonType.Number;
391                         break;
392                     case TypeCode.String:
393                     case TypeCode.Char:
394                     case TypeCode.DateTime:
395                         jsonType = JsonType.String;
396                         break;
397                     default:
398                         if (type == typeof(Uri) || type == typeof(Guid) || type == typeof(DateTimeOffset))
399                         {
400                             jsonType = JsonType.String;
401                         }
402                         else
403                         {
404                             allowedType = false;
405                         }
406
407                         break;
408                 }
409             }
410             else
411             {
412                 allowedType = false;
413             }
414
415             if (allowedType)
416             {
417                 result = new JsonPrimitive(value, jsonType);
418                 return true;
419             }
420             else
421             {
422                 result = null;
423                 return false;
424             }
425         }
426
427         /// <summary>
428         /// Attempts to convert this <see cref="System.Json.JsonPrimitive"/> instance into an instance of the specified type.
429         /// </summary>
430         /// <param name="type">The type to which the conversion is being performed.</param>
431         /// <returns>An object instance initialized with the <see cref="System.Json.JsonValue"/> value
432         /// specified if the conversion.</returns>
433         /// <exception cref="System.UriFormatException">If T is <see cref="System.Uri"/> and this value does
434         /// not represent a valid Uri.</exception>
435         /// <exception cref="OverflowException">If T is a numeric type, and a narrowing conversion would result
436         /// in a loss of data. For example, if this instance holds an <see cref="System.Int32"/> value of 10000,
437         /// and T is <see cref="System.Byte"/>, this operation would throw an <see cref="System.OverflowException"/>
438         /// because 10000 is outside the range of the <see cref="System.Byte"/> data type.</exception>
439         /// <exception cref="System.FormatException">If the conversion from the string representation of this
440         /// value into another fails because the string is not in the proper format.</exception>
441         /// <exception cref="System.InvalidCastException">If this instance cannot be read as type T.</exception>
442         public override object ReadAs(Type type)
443         {
444             if (type == null)
445             {
446                 throw new ArgumentNullException("type");
447             }
448
449             object result;
450             ReadAsFailureKind failure = TryReadAsInternal(type, out result);
451             if (failure == ReadAsFailureKind.NoFailure)
452             {
453                 return result;
454             }
455             else
456             {
457                 string valueStr = value.ToString();
458                 string typeOfTName = type.Name;
459                 switch (failure)
460                 {
461                     case ReadAsFailureKind.InvalidFormat:
462                         throw new FormatException(RS.Format(Properties.Resources.CannotReadPrimitiveAsType, valueStr, typeOfTName));
463                     case ReadAsFailureKind.InvalidDateFormat:
464                         throw new FormatException(RS.Format(Properties.Resources.InvalidDateFormat, valueStr, typeOfTName));
465                     case ReadAsFailureKind.InvalidUriFormat:
466                         throw new UriFormatException(RS.Format(Properties.Resources.InvalidUriFormat, jsonPrimitiveType.Name, valueStr, typeOfTName, uriType.Name));
467                     case ReadAsFailureKind.Overflow:
468                         throw new OverflowException(RS.Format(Properties.Resources.OverflowReadAs, valueStr, typeOfTName));
469                     case ReadAsFailureKind.InvalidCast:
470                     default:
471                         throw new InvalidCastException(RS.Format(Properties.Resources.CannotReadPrimitiveAsType, valueStr, typeOfTName));
472                 }
473             }
474         }
475
476         /// <summary>
477         /// Attempts to convert this <see cref="System.Json.JsonPrimitive"/> instance into an instance of the specified type.
478         /// </summary>
479         /// <param name="type">The type to which the conversion is being performed.</param>
480         /// <param name="value">An object instance to be initialized with this instance or null if the conversion cannot be performed.</param>
481         /// <returns>true if this <see cref="System.Json.JsonPrimitive"/> instance can be read as the specified type; otherwise, false.</returns>
482         [SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "value",
483             Justification = "field is used with 'this' and arg is out param which makes it harder to be misused.")]
484         public override bool TryReadAs(Type type, out object value)
485         {
486             return TryReadAsInternal(type, out value) == ReadAsFailureKind.NoFailure;
487         }
488
489         /// <summary>
490         /// Returns the value this object wraps (if any).
491         /// </summary>
492         /// <returns>The value wrapped by this instance or null if none.</returns>
493         internal override object Read()
494         {
495             return value;
496         }
497
498         internal override void Save(XmlDictionaryWriter jsonWriter)
499         {
500             if (value == null)
501             {
502                 throw new ArgumentNullException("jsonWriter");
503             }
504
505             switch (jsonType)
506             {
507                 case JsonType.Boolean:
508                     jsonWriter.WriteAttributeString(JXmlToJsonValueConverter.TypeAttributeName, JXmlToJsonValueConverter.BooleanAttributeValue);
509                     break;
510                 case JsonType.Number:
511                     jsonWriter.WriteAttributeString(JXmlToJsonValueConverter.TypeAttributeName, JXmlToJsonValueConverter.NumberAttributeValue);
512                     break;
513                 default:
514                     jsonWriter.WriteAttributeString(JXmlToJsonValueConverter.TypeAttributeName, JXmlToJsonValueConverter.StringAttributeValue);
515                     break;
516             }
517
518             WriteValue(jsonWriter);
519         }
520
521         private static ConvertResult StringToBool(string valueString)
522         {
523             ConvertResult result = new ConvertResult();
524             bool tempBool;
525             result.ReadAsFailureKind = Boolean.TryParse(valueString, out tempBool) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidFormat;
526             result.Value = tempBool;
527             return result;
528         }
529
530         private static ConvertResult StringToByte(string valueString)
531         {
532             ConvertResult result = new ConvertResult();
533             byte tempByte;
534             result.ReadAsFailureKind = Byte.TryParse(valueString, out tempByte) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
535             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
536             {
537                 result.ReadAsFailureKind = StringToNumberConverter<byte>(valueString, out tempByte);
538             }
539
540             result.Value = tempByte;
541             return result;
542         }
543
544         private static ConvertResult StringToChar(string valueString)
545         {
546             ConvertResult result = new ConvertResult();
547             char tempChar;
548             result.ReadAsFailureKind = Char.TryParse(valueString, out tempChar) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidFormat;
549             result.Value = tempChar;
550             return result;
551         }
552
553         private static ConvertResult StringToDecimal(string valueString)
554         {
555             ConvertResult result = new ConvertResult();
556             decimal tempDecimal;
557             result.ReadAsFailureKind = Decimal.TryParse(valueString, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out tempDecimal) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
558             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
559             {
560                 result.ReadAsFailureKind = StringToNumberConverter<decimal>(valueString, out tempDecimal);
561             }
562
563             result.Value = tempDecimal;
564             return result;
565         }
566
567         private static ConvertResult StringToDateTime(string valueString)
568         {
569             ConvertResult result = new ConvertResult();
570             DateTime tempDateTime;
571             result.ReadAsFailureKind = TryParseDateTime(valueString, out tempDateTime) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidDateFormat;
572             result.Value = tempDateTime;
573             return result;
574         }
575
576         private static ConvertResult StringToDateTimeOffset(string valueString)
577         {
578             ConvertResult result = new ConvertResult();
579             DateTimeOffset tempDateTimeOffset;
580             result.ReadAsFailureKind = TryParseDateTimeOffset(valueString, out tempDateTimeOffset) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidDateFormat;
581             result.Value = tempDateTimeOffset;
582             return result;
583         }
584
585         private static ConvertResult StringToDouble(string valueString)
586         {
587             ConvertResult result = new ConvertResult();
588             double tempDouble;
589             result.ReadAsFailureKind = Double.TryParse(valueString, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out tempDouble) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
590             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
591             {
592                 result.ReadAsFailureKind = StringToNumberConverter<double>(valueString, out tempDouble);
593             }
594
595             result.Value = tempDouble;
596             return result;
597         }
598
599         private static bool TryGuidParse (string value, out Guid guid)
600         {
601 #if NET_4_0
602                 return Guid.TryParse (value, out guid);
603 #else
604                 try {
605                         guid = new Guid (value);
606                         return true;
607                 } catch (Exception) {
608                         guid = Guid.Empty;
609                         return false;
610                 }
611 #endif
612         }
613
614         private static ConvertResult StringToGuid(string valueString)
615         {
616             ConvertResult result = new ConvertResult();
617             Guid tempGuid;
618             result.ReadAsFailureKind = TryGuidParse(valueString, out tempGuid) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidFormat;
619             result.Value = tempGuid;
620             return result;
621         }
622
623         private static ConvertResult StringToShort(string valueString)
624         {
625             ConvertResult result = new ConvertResult();
626             short tempShort;
627             result.ReadAsFailureKind = Int16.TryParse(valueString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempShort) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
628             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
629             {
630                 result.ReadAsFailureKind = StringToNumberConverter<short>(valueString, out tempShort);
631             }
632
633             result.Value = tempShort;
634             return result;
635         }
636
637         private static ConvertResult StringToInt(string valueString)
638         {
639             ConvertResult result = new ConvertResult();
640             int tempInt;
641             result.ReadAsFailureKind = Int32.TryParse(valueString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempInt) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
642             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
643             {
644                 result.ReadAsFailureKind = StringToNumberConverter<int>(valueString, out tempInt);
645             }
646
647             result.Value = tempInt;
648             return result;
649         }
650
651         private static ConvertResult StringToLong(string valueString)
652         {
653             ConvertResult result = new ConvertResult();
654             long tempLong;
655             result.ReadAsFailureKind = Int64.TryParse(valueString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempLong) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
656             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
657             {
658                 result.ReadAsFailureKind = StringToNumberConverter<long>(valueString, out tempLong);
659             }
660
661             result.Value = tempLong;
662             return result;
663         }
664
665         private static ConvertResult StringToSByte(string valueString)
666         {
667             ConvertResult result = new ConvertResult();
668             sbyte tempSByte;
669             result.ReadAsFailureKind = SByte.TryParse(valueString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempSByte) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
670             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
671             {
672                 result.ReadAsFailureKind = StringToNumberConverter<sbyte>(valueString, out tempSByte);
673             }
674
675             result.Value = tempSByte;
676             return result;
677         }
678
679         private static ConvertResult StringToFloat(string valueString)
680         {
681             ConvertResult result = new ConvertResult();
682             float tempFloat;
683             result.ReadAsFailureKind = Single.TryParse(valueString, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out tempFloat) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
684             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
685             {
686                 result.ReadAsFailureKind = StringToNumberConverter<float>(valueString, out tempFloat);
687             }
688
689             result.Value = tempFloat;
690             return result;
691         }
692
693         private static ConvertResult StringToUShort(string valueString)
694         {
695             ConvertResult result = new ConvertResult();
696             ushort tempUShort;
697             result.ReadAsFailureKind = UInt16.TryParse(valueString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempUShort) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
698             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
699             {
700                 result.ReadAsFailureKind = StringToNumberConverter<ushort>(valueString, out tempUShort);
701             }
702
703             result.Value = tempUShort;
704             return result;
705         }
706
707         private static ConvertResult StringToUInt(string valueString)
708         {
709             ConvertResult result = new ConvertResult();
710             uint tempUInt;
711             result.ReadAsFailureKind = UInt32.TryParse(valueString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempUInt) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
712             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
713             {
714                 result.ReadAsFailureKind = StringToNumberConverter<uint>(valueString, out tempUInt);
715             }
716
717             result.Value = tempUInt;
718             return result;
719         }
720
721         private static ConvertResult StringToULong(string valueString)
722         {
723             ConvertResult result = new ConvertResult();
724             ulong tempULong;
725             result.ReadAsFailureKind = UInt64.TryParse(valueString, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempULong) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidCast;
726             if (result.ReadAsFailureKind != ReadAsFailureKind.NoFailure)
727             {
728                 result.ReadAsFailureKind = StringToNumberConverter<ulong>(valueString, out tempULong);
729             }
730
731             result.Value = tempULong;
732             return result;
733         }
734
735         private static ConvertResult StringToUri(string valueString)
736         {
737             ConvertResult result = new ConvertResult();
738             Uri tempUri;
739             result.ReadAsFailureKind = Uri.TryCreate(valueString, UriKind.RelativeOrAbsolute, out tempUri) ? ReadAsFailureKind.NoFailure : ReadAsFailureKind.InvalidUriFormat;
740             result.Value = tempUri;
741             return result;
742         }
743
744         private static ReadAsFailureKind StringToNumberConverter<T>(string valueString, out T valueNumber)
745         {
746             string str = valueString.Trim();
747
748             if (str.IndexOfAny(FloatingPointChars) < 0)
749             {
750                 long longVal;
751                 if (Int64.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out longVal))
752                 {
753                     return NumberToNumberConverter<T>(longVal, out valueNumber);
754                 }
755             }
756
757             decimal decValue;
758             if (Decimal.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out decValue) && decValue != 0)
759             {
760                 return NumberToNumberConverter<T>(decValue, out valueNumber);
761             }
762
763             double dblValue;
764             if (Double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out dblValue))
765             {
766                 return NumberToNumberConverter<T>(dblValue, out valueNumber);
767             }
768
769             valueNumber = default(T);
770             return ReadAsFailureKind.InvalidFormat;
771         }
772
773         private static ReadAsFailureKind NumberToNumberConverter<T>(object valueObject, out T valueNumber)
774         {
775             object value;
776             ReadAsFailureKind failureKind = NumberToNumberConverter(typeof(T), valueObject, out value);
777             if (failureKind == ReadAsFailureKind.NoFailure)
778             {
779                 valueNumber = (T)value;
780             }
781             else
782             {
783                 valueNumber = default(T);
784             }
785
786             return failureKind;
787         }
788
789         private static ReadAsFailureKind NumberToNumberConverter(Type type, object valueObject, out object valueNumber)
790         {
791             try
792             {
793                 valueNumber = System.Convert.ChangeType(valueObject, type, CultureInfo.InvariantCulture);
794                 return ReadAsFailureKind.NoFailure;
795             }
796             catch (OverflowException)
797             {
798                 valueNumber = null;
799                 return ReadAsFailureKind.Overflow;
800             }
801         }
802
803         private static bool TryParseDateTime(string valueString, out DateTime dateTime)
804         {
805             string filteredValue = valueString.EndsWith(UtcString, StringComparison.Ordinal) ? valueString.Replace(UtcString, GmtString) : valueString;
806
807             if (DateTime.TryParse(filteredValue, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTime))
808             {
809                 return true;
810             }
811
812             if (TryParseAspNetDateTimeFormat(valueString, out dateTime))
813             {
814                 return true;
815             }
816
817             return false;
818         }
819
820         private static bool TryParseDateTimeOffset(string valueString, out DateTimeOffset dateTimeOffset)
821         {
822             string filteredValue = valueString.EndsWith(UtcString, StringComparison.Ordinal) ? valueString.Replace(UtcString, GmtString) : valueString;
823
824             if (DateTimeOffset.TryParse(filteredValue, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset))
825             {
826                 return true;
827             }
828
829             if (TryParseAspNetDateTimeFormat(valueString, out dateTimeOffset))
830             {
831                 return true;
832             }
833
834             return false;
835         }
836
837         private static bool TryParseAspNetDateTimeFormat(string valueString, out DateTime dateTime)
838         {
839             // Reference to the format is available at these sources:
840             // http://msdn.microsoft.com/en-us/library/bb299886.aspx#intro_to_json_sidebarb
841             // http://msdn.microsoft.com/en-us/library/bb412170.aspx
842
843             // The format for the value is given by the following regex:
844             // \/Date\((?<milliseconds>\-?\d+)(?<offset>[\+\-]?\d{4})\)\/
845             // where milliseconds is the number of milliseconds since 1970/01/01:00:00:00.000 UTC (the "unix baseline")
846             // and offset is an optional which indicates whether the value is local or UTC.
847             // The actual value of the offset is ignored, since the ticks represent the UTC offset. The value is converted to local time based on that info.
848             const string DateTimePrefix = "/Date(";
849             const int DateTimePrefixLength = 6;
850             const string DateTimeSuffix = ")/";
851             const int DateTimeSuffixLength = 2;
852
853             if (valueString.StartsWith(DateTimePrefix, StringComparison.Ordinal) && valueString.EndsWith(DateTimeSuffix, StringComparison.Ordinal))
854             {
855                 string ticksValue = valueString.Substring(DateTimePrefixLength, valueString.Length - DateTimePrefixLength - DateTimeSuffixLength);
856                 DateTimeKind dateTimeKind = DateTimeKind.Utc;
857
858                 int indexOfTimeZoneOffset = ticksValue.IndexOf('+', 1);
859
860                 if (indexOfTimeZoneOffset < 0)
861                 {
862                     indexOfTimeZoneOffset = ticksValue.IndexOf('-', 1);
863                 }
864
865                 // If an offset is present, verify it is properly formatted. Actual value is ignored (see spec).
866                 if (indexOfTimeZoneOffset != -1)
867                 {
868                     if (indexOfTimeZoneOffset + 5 == ticksValue.Length
869                         && IsLatinDigit(ticksValue[indexOfTimeZoneOffset + 1])
870                         && IsLatinDigit(ticksValue[indexOfTimeZoneOffset + 2])
871                         && IsLatinDigit(ticksValue[indexOfTimeZoneOffset + 3])
872                         && IsLatinDigit(ticksValue[indexOfTimeZoneOffset + 4]))
873                     {
874                         ticksValue = ticksValue.Substring(0, indexOfTimeZoneOffset);
875                         dateTimeKind = DateTimeKind.Local;
876                     }
877                     else
878                     {
879                         dateTime = new DateTime();
880                         return false;
881                     }
882                 }
883
884                 long millisecondsSinceUnixEpoch;
885                 if (Int64.TryParse(ticksValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out millisecondsSinceUnixEpoch))
886                 {
887                     long ticks = (millisecondsSinceUnixEpoch * 10000) + UnixEpochTicks;
888                     if (ticks < DateTime.MaxValue.Ticks)
889                     {
890                         dateTime = new DateTime(ticks, DateTimeKind.Utc);
891                         if (dateTimeKind == DateTimeKind.Local)
892                         {
893                             dateTime = dateTime.ToLocalTime();
894                         }
895
896                         return true;
897                     }
898                 }
899             }
900
901             dateTime = new DateTime();
902             return false;
903         }
904
905         private static bool TryParseAspNetDateTimeFormat(string valueString, out DateTimeOffset dateTimeOffset)
906         {
907             DateTime dateTime;
908             if (TryParseAspNetDateTimeFormat(valueString, out dateTime))
909             {
910                 dateTimeOffset = new DateTimeOffset(dateTime);
911                 return true;
912             }
913
914             dateTimeOffset = new DateTimeOffset();
915             return false;
916         }
917
918         private static bool IsLatinDigit(char c)
919         {
920             return (c >= '0') && (c <= '9');
921         }
922
923         private static string UnescapeJsonString(string val)
924         {
925             if (val == null)
926             {
927                 return null;
928             }
929
930             StringBuilder sb = null;
931             int startIndex = 0, count = 0;
932             for (int i = 0; i < val.Length; i++)
933             {
934                 if (val[i] == '\\')
935                 {
936                     i++;
937                     if (sb == null)
938                     {
939                         sb = new StringBuilder();
940                     }
941
942                     sb.Append(val, startIndex, count);
943 #if NET_4_0
944                     Contract.Assert(i < val.Length, "Found that a '\' was the last character in a string, which is invalid JSON. Verify the calling method uses a valid JSON string as the input parameter of this method.");
945 #endif
946                     switch (val[i])
947                     {
948                         case '"':
949                         case '\'':
950                         case '/':
951                         case '\\':
952                             sb.Append(val[i]);
953                             break;
954                         case 'b':
955                             sb.Append('\b');
956                             break;
957                         case 'f':
958                             sb.Append('\f');
959                             break;
960                         case 'n':
961                             sb.Append('\n');
962                             break;
963                         case 'r':
964                             sb.Append('\r');
965                             break;
966                         case 't':
967                             sb.Append('\t');
968                             break;
969                         case 'u':
970 #if NET_4_0
971                             Contract.Assert((i + 3) < val.Length, String.Format(CultureInfo.CurrentCulture, "Unexpected char {0} at position {1}. The unicode escape sequence should be followed by 4 digits.", val[i], i));
972 #endif
973                             sb.Append(ParseChar(val.Substring(i + 1, 4), NumberStyles.HexNumber));
974                             i += 4;
975                             break;
976                     }
977
978                     startIndex = i + 1;
979                     count = 0;
980                 }
981                 else
982                 {
983                     count++;
984                 }
985             }
986
987             if (sb == null)
988             {
989                 return val;
990             }
991
992             if (count > 0)
993             {
994                 sb.Append(val, startIndex, count);
995             }
996
997             return sb.ToString();
998         }
999
1000         private static char ParseChar(string value, NumberStyles style)
1001         {
1002             try
1003             {
1004                 int intValue = Int32.Parse(value, style, NumberFormatInfo.InvariantInfo);
1005                 return System.Convert.ToChar(intValue);
1006             }
1007             catch (ArgumentException exception)
1008             {
1009                 throw new InvalidCastException(exception.Message, exception);
1010             }
1011             catch (FormatException exception)
1012             {
1013                 throw new InvalidCastException(exception.Message, exception);
1014             }
1015             catch (OverflowException exception)
1016             {
1017                 throw new InvalidCastException(exception.Message, exception);
1018             }
1019         }
1020
1021         [SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "value",
1022             Justification = "field is used with 'this' and arg is out param which makes it harder to be misused.")]
1023         private ReadAsFailureKind TryReadAsInternal(Type type, out object value)
1024         {
1025             if (base.TryReadAs(type, out value))
1026             {
1027                 return ReadAsFailureKind.NoFailure;
1028             }
1029
1030             if (type == this.value.GetType())
1031             {
1032                 value = this.value;
1033                 return ReadAsFailureKind.NoFailure;
1034             }
1035
1036             if (jsonType == JsonType.Number)
1037             {
1038                 switch (Type.GetTypeCode(type))
1039                 {
1040                     case TypeCode.Byte:
1041                     case TypeCode.SByte:
1042                     case TypeCode.Int16:
1043                     case TypeCode.Int32:
1044                     case TypeCode.Int64:
1045                     case TypeCode.UInt16:
1046                     case TypeCode.UInt32:
1047                     case TypeCode.UInt64:
1048                     case TypeCode.Single:
1049                     case TypeCode.Double:
1050                     case TypeCode.Decimal:
1051                         return NumberToNumberConverter(type, this.value, out value);
1052                     case TypeCode.String:
1053                         value = ToString();
1054                         return ReadAsFailureKind.NoFailure;
1055                 }
1056             }
1057
1058             if (jsonType == JsonType.Boolean)
1059             {
1060                 if (type == typeof(string))
1061                 {
1062                     value = ToString();
1063                     return ReadAsFailureKind.NoFailure;
1064                 }
1065             }
1066
1067             if (jsonType == JsonType.String)
1068             {
1069                 string str = UnescapeJsonString(ToString());
1070 #if NET_4_0
1071                 Contract.Assert(str.Length >= 2 && str.StartsWith("\"", StringComparison.Ordinal) && str.EndsWith("\"", StringComparison.Ordinal), "The unescaped string must begin and end with quotes.");
1072 #endif
1073                 str = str.Substring(1, str.Length - 2);
1074
1075                 if (stringConverters.ContainsKey(type))
1076                 {
1077                     ConvertResult result = stringConverters[type].Invoke(str);
1078                     value = result.Value;
1079                     return result.ReadAsFailureKind;
1080                 }
1081
1082                 if (type == typeof(string))
1083                 {
1084                     value = str;
1085                     return ReadAsFailureKind.NoFailure;
1086                 }
1087             }
1088
1089             value = null;
1090             return ReadAsFailureKind.InvalidCast;
1091         }
1092
1093         private void WriteValue(XmlDictionaryWriter jsonWriter)
1094         {
1095             Type valueType = value.GetType();
1096             switch (Type.GetTypeCode(valueType))
1097             {
1098                 case TypeCode.Boolean:
1099                     jsonWriter.WriteValue((bool)value);
1100                     break;
1101                 case TypeCode.Byte:
1102                 case TypeCode.Int16:
1103                 case TypeCode.Int32:
1104                 case TypeCode.Int64:
1105                 case TypeCode.SByte:
1106                 case TypeCode.UInt16:
1107                 case TypeCode.UInt32:
1108                 case TypeCode.UInt64:
1109                 case TypeCode.Decimal:
1110                     jsonWriter.WriteValue(String.Format(CultureInfo.InvariantCulture, "{0}", value));
1111                     break;
1112                 case TypeCode.Single:
1113                 case TypeCode.Double:
1114                     jsonWriter.WriteValue(String.Format(CultureInfo.InvariantCulture, "{0:R}", value));
1115                     break;
1116                 case TypeCode.Char:
1117                     jsonWriter.WriteValue(new string((char)value, 1));
1118                     break;
1119                 case TypeCode.String:
1120                     jsonWriter.WriteValue((string)value);
1121                     break;
1122                 case TypeCode.DateTime:
1123                     jsonWriter.WriteValue(((DateTime)value).ToString(DateTimeIsoFormat, CultureInfo.InvariantCulture));
1124                     break;
1125                 default:
1126                     if (valueType == typeof(Uri))
1127                     {
1128                         Uri uri = (Uri)value;
1129                         jsonWriter.WriteValue(uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped));
1130                     }
1131                     else if (valueType == typeof(DateTimeOffset))
1132                     {
1133                         jsonWriter.WriteValue(((DateTimeOffset)value).ToString(DateTimeIsoFormat, CultureInfo.InvariantCulture));
1134                     }
1135                     else
1136                     {
1137                         jsonWriter.WriteValue(value);
1138                     }
1139
1140                     break;
1141             }
1142         }
1143
1144         private class ConvertResult
1145         {
1146             public ReadAsFailureKind ReadAsFailureKind { get; set; }
1147
1148             public object Value { get; set; }
1149         }
1150     }
1151 }