corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | #region Header |
2 | /* |
||
3 | * JsonMapper.cs |
||
4 | * JSON to .Net object and object to JSON conversions. |
||
5 | * |
||
6 | * The authors disclaim copyright to this source code. For more details, see |
||
7 | * the COPYING file included with this distribution. |
||
8 | */ |
||
9 | #endregion |
||
10 | |||
11 | |||
12 | using System; |
||
13 | using System.Collections; |
||
14 | using System.Collections.Generic; |
||
15 | using System.Globalization; |
||
16 | using System.IO; |
||
17 | using System.Reflection; |
||
18 | |||
19 | |||
20 | namespace LitJson |
||
21 | { |
||
22 | internal struct PropertyMetadata |
||
23 | { |
||
24 | public MemberInfo Info; |
||
25 | public bool IsField; |
||
26 | public Type Type; |
||
27 | } |
||
28 | |||
29 | |||
30 | internal struct ArrayMetadata |
||
31 | { |
||
32 | private Type element_type; |
||
33 | private bool is_array; |
||
34 | private bool is_list; |
||
35 | |||
36 | |||
37 | public Type ElementType { |
||
38 | get { |
||
39 | if (element_type == null) |
||
40 | return typeof (JsonData); |
||
41 | |||
42 | return element_type; |
||
43 | } |
||
44 | |||
45 | set { element_type = value; } |
||
46 | } |
||
47 | |||
48 | public bool IsArray { |
||
49 | get { return is_array; } |
||
50 | set { is_array = value; } |
||
51 | } |
||
52 | |||
53 | public bool IsList { |
||
54 | get { return is_list; } |
||
55 | set { is_list = value; } |
||
56 | } |
||
57 | } |
||
58 | |||
59 | |||
60 | internal struct ObjectMetadata |
||
61 | { |
||
62 | private Type element_type; |
||
63 | private bool is_dictionary; |
||
64 | |||
65 | private IDictionary<string, PropertyMetadata> properties; |
||
66 | |||
67 | |||
68 | public Type ElementType { |
||
69 | get { |
||
70 | if (element_type == null) |
||
71 | return typeof (JsonData); |
||
72 | |||
73 | return element_type; |
||
74 | } |
||
75 | |||
76 | set { element_type = value; } |
||
77 | } |
||
78 | |||
79 | public bool IsDictionary { |
||
80 | get { return is_dictionary; } |
||
81 | set { is_dictionary = value; } |
||
82 | } |
||
83 | |||
84 | public IDictionary<string, PropertyMetadata> Properties { |
||
85 | get { return properties; } |
||
86 | set { properties = value; } |
||
87 | } |
||
88 | } |
||
89 | |||
90 | |||
91 | internal delegate void ExporterFunc (object obj, JsonWriter writer); |
||
92 | public delegate void ExporterFunc<T> (T obj, JsonWriter writer); |
||
93 | |||
94 | internal delegate object ImporterFunc (object input); |
||
95 | public delegate TValue ImporterFunc<TJson, TValue> (TJson input); |
||
96 | |||
97 | public delegate IJsonWrapper WrapperFactory (); |
||
98 | |||
99 | |||
100 | public class JsonMapper |
||
101 | { |
||
102 | #region Fields |
||
103 | private static int max_nesting_depth; |
||
104 | |||
105 | private static IFormatProvider datetime_format; |
||
106 | |||
107 | private static IDictionary<Type, ExporterFunc> base_exporters_table; |
||
108 | private static IDictionary<Type, ExporterFunc> custom_exporters_table; |
||
109 | |||
110 | private static IDictionary<Type, |
||
111 | IDictionary<Type, ImporterFunc>> base_importers_table; |
||
112 | private static IDictionary<Type, |
||
113 | IDictionary<Type, ImporterFunc>> custom_importers_table; |
||
114 | |||
115 | private static IDictionary<Type, ArrayMetadata> array_metadata; |
||
116 | private static readonly object array_metadata_lock = new Object (); |
||
117 | |||
118 | private static IDictionary<Type, |
||
119 | IDictionary<Type, MethodInfo>> conv_ops; |
||
120 | private static readonly object conv_ops_lock = new Object (); |
||
121 | |||
122 | private static IDictionary<Type, ObjectMetadata> object_metadata; |
||
123 | private static readonly object object_metadata_lock = new Object (); |
||
124 | |||
125 | private static IDictionary<Type, |
||
126 | IList<PropertyMetadata>> type_properties; |
||
127 | private static readonly object type_properties_lock = new Object (); |
||
128 | |||
129 | private static JsonWriter static_writer; |
||
130 | private static readonly object static_writer_lock = new Object (); |
||
131 | #endregion |
||
132 | |||
133 | |||
134 | #region Constructors |
||
135 | static JsonMapper () |
||
136 | { |
||
137 | max_nesting_depth = 100; |
||
138 | |||
139 | array_metadata = new Dictionary<Type, ArrayMetadata> (); |
||
140 | conv_ops = new Dictionary<Type, IDictionary<Type, MethodInfo>> (); |
||
141 | object_metadata = new Dictionary<Type, ObjectMetadata> (); |
||
142 | type_properties = new Dictionary<Type, |
||
143 | IList<PropertyMetadata>> (); |
||
144 | |||
145 | static_writer = new JsonWriter (); |
||
146 | |||
147 | datetime_format = DateTimeFormatInfo.InvariantInfo; |
||
148 | |||
149 | base_exporters_table = new Dictionary<Type, ExporterFunc> (); |
||
150 | custom_exporters_table = new Dictionary<Type, ExporterFunc> (); |
||
151 | |||
152 | base_importers_table = new Dictionary<Type, |
||
153 | IDictionary<Type, ImporterFunc>> (); |
||
154 | custom_importers_table = new Dictionary<Type, |
||
155 | IDictionary<Type, ImporterFunc>> (); |
||
156 | |||
157 | RegisterBaseExporters (); |
||
158 | RegisterBaseImporters (); |
||
159 | } |
||
160 | #endregion |
||
161 | |||
162 | |||
163 | #region Private Methods |
||
164 | private static void AddArrayMetadata (Type type) |
||
165 | { |
||
166 | if (array_metadata.ContainsKey (type)) |
||
167 | return; |
||
168 | |||
169 | ArrayMetadata data = new ArrayMetadata (); |
||
170 | |||
171 | data.IsArray = type.IsArray; |
||
172 | |||
173 | if (type.GetInterface ("System.Collections.IList") != null) |
||
174 | data.IsList = true; |
||
175 | |||
176 | foreach (PropertyInfo p_info in type.GetProperties ()) { |
||
177 | if (p_info.Name != "Item") |
||
178 | continue; |
||
179 | |||
180 | ParameterInfo[] parameters = p_info.GetIndexParameters (); |
||
181 | |||
182 | if (parameters.Length != 1) |
||
183 | continue; |
||
184 | |||
185 | if (parameters[0].ParameterType == typeof (int)) |
||
186 | data.ElementType = p_info.PropertyType; |
||
187 | } |
||
188 | |||
189 | lock (array_metadata_lock) { |
||
190 | try { |
||
191 | array_metadata.Add (type, data); |
||
192 | } catch (ArgumentException) { |
||
193 | return; |
||
194 | } |
||
195 | } |
||
196 | } |
||
197 | |||
198 | private static void AddObjectMetadata (Type type) |
||
199 | { |
||
200 | if (object_metadata.ContainsKey (type)) |
||
201 | return; |
||
202 | |||
203 | ObjectMetadata data = new ObjectMetadata (); |
||
204 | |||
205 | if (type.GetInterface ("System.Collections.IDictionary") != null) |
||
206 | data.IsDictionary = true; |
||
207 | |||
208 | data.Properties = new Dictionary<string, PropertyMetadata> (); |
||
209 | |||
210 | foreach (PropertyInfo p_info in type.GetProperties ()) { |
||
211 | if (p_info.Name == "Item") { |
||
212 | ParameterInfo[] parameters = p_info.GetIndexParameters (); |
||
213 | |||
214 | if (parameters.Length != 1) |
||
215 | continue; |
||
216 | |||
217 | if (parameters[0].ParameterType == typeof (string)) |
||
218 | data.ElementType = p_info.PropertyType; |
||
219 | |||
220 | continue; |
||
221 | } |
||
222 | |||
223 | PropertyMetadata p_data = new PropertyMetadata (); |
||
224 | p_data.Info = p_info; |
||
225 | p_data.Type = p_info.PropertyType; |
||
226 | |||
227 | data.Properties.Add (p_info.Name, p_data); |
||
228 | } |
||
229 | |||
230 | foreach (FieldInfo f_info in type.GetFields ()) { |
||
231 | PropertyMetadata p_data = new PropertyMetadata (); |
||
232 | p_data.Info = f_info; |
||
233 | p_data.IsField = true; |
||
234 | p_data.Type = f_info.FieldType; |
||
235 | |||
236 | data.Properties.Add (f_info.Name, p_data); |
||
237 | } |
||
238 | |||
239 | lock (object_metadata_lock) { |
||
240 | try { |
||
241 | object_metadata.Add (type, data); |
||
242 | } catch (ArgumentException) { |
||
243 | return; |
||
244 | } |
||
245 | } |
||
246 | } |
||
247 | |||
248 | private static void AddTypeProperties (Type type) |
||
249 | { |
||
250 | if (type_properties.ContainsKey (type)) |
||
251 | return; |
||
252 | |||
253 | IList<PropertyMetadata> props = new List<PropertyMetadata> (); |
||
254 | |||
255 | foreach (PropertyInfo p_info in type.GetProperties ()) { |
||
256 | if (p_info.Name == "Item") |
||
257 | continue; |
||
258 | |||
259 | PropertyMetadata p_data = new PropertyMetadata (); |
||
260 | p_data.Info = p_info; |
||
261 | p_data.IsField = false; |
||
262 | props.Add (p_data); |
||
263 | } |
||
264 | |||
265 | foreach (FieldInfo f_info in type.GetFields ()) { |
||
266 | PropertyMetadata p_data = new PropertyMetadata (); |
||
267 | p_data.Info = f_info; |
||
268 | p_data.IsField = true; |
||
269 | |||
270 | props.Add (p_data); |
||
271 | } |
||
272 | |||
273 | lock (type_properties_lock) { |
||
274 | try { |
||
275 | type_properties.Add (type, props); |
||
276 | } catch (ArgumentException) { |
||
277 | return; |
||
278 | } |
||
279 | } |
||
280 | } |
||
281 | |||
282 | private static MethodInfo GetConvOp (Type t1, Type t2) |
||
283 | { |
||
284 | lock (conv_ops_lock) { |
||
285 | if (! conv_ops.ContainsKey (t1)) |
||
286 | conv_ops.Add (t1, new Dictionary<Type, MethodInfo> ()); |
||
287 | } |
||
288 | |||
289 | if (conv_ops[t1].ContainsKey (t2)) |
||
290 | return conv_ops[t1][t2]; |
||
291 | |||
292 | MethodInfo op = t1.GetMethod ( |
||
293 | "op_Implicit", new Type[] { t2 }); |
||
294 | |||
295 | lock (conv_ops_lock) { |
||
296 | try { |
||
297 | conv_ops[t1].Add (t2, op); |
||
298 | } catch (ArgumentException) { |
||
299 | return conv_ops[t1][t2]; |
||
300 | } |
||
301 | } |
||
302 | |||
303 | return op; |
||
304 | } |
||
305 | |||
306 | private static object ReadValue (Type inst_type, JsonReader reader) |
||
307 | { |
||
308 | reader.Read (); |
||
309 | |||
310 | if (reader.Token == JsonToken.ArrayEnd) |
||
311 | return null; |
||
312 | |||
313 | if (reader.Token == JsonToken.Null) { |
||
314 | |||
315 | if (! inst_type.IsClass) |
||
316 | throw new JsonException (String.Format ( |
||
317 | "Can't assign null to an instance of type {0}", |
||
318 | inst_type)); |
||
319 | |||
320 | return null; |
||
321 | } |
||
322 | |||
323 | if (reader.Token == JsonToken.Double || |
||
324 | reader.Token == JsonToken.Int || |
||
325 | reader.Token == JsonToken.Long || |
||
326 | reader.Token == JsonToken.String || |
||
327 | reader.Token == JsonToken.Boolean) { |
||
328 | |||
329 | Type json_type = reader.Value.GetType (); |
||
330 | |||
331 | if (inst_type.IsAssignableFrom (json_type)) |
||
332 | return reader.Value; |
||
333 | |||
334 | // If there's a custom importer that fits, use it |
||
335 | if (custom_importers_table.ContainsKey (json_type) && |
||
336 | custom_importers_table[json_type].ContainsKey ( |
||
337 | inst_type)) { |
||
338 | |||
339 | ImporterFunc importer = |
||
340 | custom_importers_table[json_type][inst_type]; |
||
341 | |||
342 | return importer (reader.Value); |
||
343 | } |
||
344 | |||
345 | // Maybe there's a base importer that works |
||
346 | if (base_importers_table.ContainsKey (json_type) && |
||
347 | base_importers_table[json_type].ContainsKey ( |
||
348 | inst_type)) { |
||
349 | |||
350 | ImporterFunc importer = |
||
351 | base_importers_table[json_type][inst_type]; |
||
352 | |||
353 | return importer (reader.Value); |
||
354 | } |
||
355 | |||
356 | // Maybe it's an enum |
||
357 | if (inst_type.IsEnum) |
||
358 | return Enum.ToObject (inst_type, reader.Value); |
||
359 | |||
360 | // Try using an implicit conversion operator |
||
361 | MethodInfo conv_op = GetConvOp (inst_type, json_type); |
||
362 | |||
363 | if (conv_op != null) |
||
364 | return conv_op.Invoke (null, |
||
365 | new object[] { reader.Value }); |
||
366 | |||
367 | // No luck |
||
368 | throw new JsonException (String.Format ( |
||
369 | "Can't assign value '{0}' (type {1}) to type {2}", |
||
370 | reader.Value, json_type, inst_type)); |
||
371 | } |
||
372 | |||
373 | object instance = null; |
||
374 | |||
375 | if (reader.Token == JsonToken.ArrayStart) { |
||
376 | |||
377 | AddArrayMetadata (inst_type); |
||
378 | ArrayMetadata t_data = array_metadata[inst_type]; |
||
379 | |||
380 | if (! t_data.IsArray && ! t_data.IsList) |
||
381 | throw new JsonException (String.Format ( |
||
382 | "Type {0} can't act as an array", |
||
383 | inst_type)); |
||
384 | |||
385 | IList list; |
||
386 | Type elem_type; |
||
387 | |||
388 | if (! t_data.IsArray) { |
||
389 | list = (IList) Activator.CreateInstance (inst_type); |
||
390 | elem_type = t_data.ElementType; |
||
391 | } else { |
||
392 | list = new ArrayList (); |
||
393 | elem_type = inst_type.GetElementType (); |
||
394 | } |
||
395 | |||
396 | while (true) { |
||
397 | object item = ReadValue (elem_type, reader); |
||
398 | if (reader.Token == JsonToken.ArrayEnd) |
||
399 | break; |
||
400 | |||
401 | list.Add (item); |
||
402 | } |
||
403 | |||
404 | if (t_data.IsArray) { |
||
405 | int n = list.Count; |
||
406 | instance = Array.CreateInstance (elem_type, n); |
||
407 | |||
408 | for (int i = 0; i < n; i++) |
||
409 | ((Array) instance).SetValue (list[i], i); |
||
410 | } else |
||
411 | instance = list; |
||
412 | |||
413 | } else if (reader.Token == JsonToken.ObjectStart) { |
||
414 | |||
415 | AddObjectMetadata (inst_type); |
||
416 | ObjectMetadata t_data = object_metadata[inst_type]; |
||
417 | |||
418 | instance = Activator.CreateInstance (inst_type); |
||
419 | |||
420 | while (true) { |
||
421 | reader.Read (); |
||
422 | |||
423 | if (reader.Token == JsonToken.ObjectEnd) |
||
424 | break; |
||
425 | |||
426 | string property = (string) reader.Value; |
||
427 | |||
428 | if (t_data.Properties.ContainsKey (property)) { |
||
429 | PropertyMetadata prop_data = |
||
430 | t_data.Properties[property]; |
||
431 | |||
432 | if (prop_data.IsField) { |
||
433 | ((FieldInfo) prop_data.Info).SetValue ( |
||
434 | instance, ReadValue (prop_data.Type, reader)); |
||
435 | } else { |
||
436 | PropertyInfo p_info = |
||
437 | (PropertyInfo) prop_data.Info; |
||
438 | |||
439 | if (p_info.CanWrite) |
||
440 | p_info.SetValue ( |
||
441 | instance, |
||
442 | ReadValue (prop_data.Type, reader), |
||
443 | null); |
||
444 | else |
||
445 | ReadValue (prop_data.Type, reader); |
||
446 | } |
||
447 | |||
448 | } else { |
||
449 | if (! t_data.IsDictionary) |
||
450 | throw new JsonException (String.Format ( |
||
451 | "The type {0} doesn't have the " + |
||
452 | "property '{1}'", inst_type, property)); |
||
453 | |||
454 | ((IDictionary) instance).Add ( |
||
455 | property, ReadValue ( |
||
456 | t_data.ElementType, reader)); |
||
457 | } |
||
458 | |||
459 | } |
||
460 | |||
461 | } |
||
462 | |||
463 | return instance; |
||
464 | } |
||
465 | |||
466 | private static IJsonWrapper ReadValue (WrapperFactory factory, |
||
467 | JsonReader reader) |
||
468 | { |
||
469 | reader.Read (); |
||
470 | |||
471 | if (reader.Token == JsonToken.ArrayEnd || |
||
472 | reader.Token == JsonToken.Null) |
||
473 | return null; |
||
474 | |||
475 | IJsonWrapper instance = factory (); |
||
476 | |||
477 | switch (reader.Token) |
||
478 | { |
||
479 | case JsonToken.String: |
||
480 | instance.SetString ((string) reader.Value); |
||
481 | break; |
||
482 | case JsonToken.Double: |
||
483 | instance.SetDouble ((double) reader.Value); |
||
484 | break; |
||
485 | case JsonToken.Int: |
||
486 | instance.SetInt ((int) reader.Value); |
||
487 | break; |
||
488 | case JsonToken.Long: |
||
489 | instance.SetLong ((long) reader.Value); |
||
490 | break; |
||
491 | case JsonToken.Boolean: |
||
492 | instance.SetBoolean ((bool) reader.Value); |
||
493 | break; |
||
494 | case JsonToken.ArrayStart: |
||
495 | instance.SetJsonType (JsonType.Array); |
||
496 | |||
497 | while (true) { |
||
498 | IJsonWrapper item = ReadValue (factory, reader); |
||
499 | if (item == null && reader.Token == JsonToken.ArrayEnd) |
||
500 | break; |
||
501 | |||
502 | ((IList) instance).Add (item); |
||
503 | } |
||
504 | break; |
||
505 | case JsonToken.ObjectStart: |
||
506 | instance.SetJsonType (JsonType.Object); |
||
507 | |||
508 | while (true) { |
||
509 | reader.Read (); |
||
510 | if (reader.Token == JsonToken.ObjectEnd) |
||
511 | break; |
||
512 | |||
513 | string property = (string) reader.Value; |
||
514 | ((IDictionary) instance)[property] = ReadValue ( |
||
515 | factory, reader); |
||
516 | } |
||
517 | break; |
||
518 | } |
||
519 | |||
520 | return instance; |
||
521 | } |
||
522 | |||
523 | private static void RegisterBaseExporters () |
||
524 | { |
||
525 | base_exporters_table[typeof (byte)] = |
||
526 | delegate (object obj, JsonWriter writer) { |
||
527 | writer.Write (Convert.ToInt32 ((byte) obj)); |
||
528 | }; |
||
529 | |||
530 | base_exporters_table[typeof (char)] = |
||
531 | delegate (object obj, JsonWriter writer) { |
||
532 | writer.Write (Convert.ToString ((char) obj)); |
||
533 | }; |
||
534 | |||
535 | base_exporters_table[typeof (DateTime)] = |
||
536 | delegate (object obj, JsonWriter writer) { |
||
537 | writer.Write (Convert.ToString ((DateTime) obj, |
||
538 | datetime_format)); |
||
539 | }; |
||
540 | |||
541 | base_exporters_table[typeof (decimal)] = |
||
542 | delegate (object obj, JsonWriter writer) { |
||
543 | writer.Write ((decimal) obj); |
||
544 | }; |
||
545 | |||
546 | base_exporters_table[typeof (sbyte)] = |
||
547 | delegate (object obj, JsonWriter writer) { |
||
548 | writer.Write (Convert.ToInt32 ((sbyte) obj)); |
||
549 | }; |
||
550 | |||
551 | base_exporters_table[typeof (short)] = |
||
552 | delegate (object obj, JsonWriter writer) { |
||
553 | writer.Write (Convert.ToInt32 ((short) obj)); |
||
554 | }; |
||
555 | |||
556 | base_exporters_table[typeof (ushort)] = |
||
557 | delegate (object obj, JsonWriter writer) { |
||
558 | writer.Write (Convert.ToInt32 ((ushort) obj)); |
||
559 | }; |
||
560 | |||
561 | base_exporters_table[typeof (uint)] = |
||
562 | delegate (object obj, JsonWriter writer) { |
||
563 | writer.Write (Convert.ToUInt64 ((uint) obj)); |
||
564 | }; |
||
565 | |||
566 | base_exporters_table[typeof (ulong)] = |
||
567 | delegate (object obj, JsonWriter writer) { |
||
568 | writer.Write ((ulong) obj); |
||
569 | }; |
||
570 | } |
||
571 | |||
572 | private static void RegisterBaseImporters () |
||
573 | { |
||
574 | ImporterFunc importer; |
||
575 | |||
576 | importer = delegate (object input) { |
||
577 | return Convert.ToByte ((int) input); |
||
578 | }; |
||
579 | RegisterImporter (base_importers_table, typeof (int), |
||
580 | typeof (byte), importer); |
||
581 | |||
582 | importer = delegate (object input) { |
||
583 | return Convert.ToUInt64 ((int) input); |
||
584 | }; |
||
585 | RegisterImporter (base_importers_table, typeof (int), |
||
586 | typeof (ulong), importer); |
||
587 | |||
588 | importer = delegate (object input) { |
||
589 | return Convert.ToSByte ((int) input); |
||
590 | }; |
||
591 | RegisterImporter (base_importers_table, typeof (int), |
||
592 | typeof (sbyte), importer); |
||
593 | |||
594 | importer = delegate (object input) { |
||
595 | return Convert.ToInt16 ((int) input); |
||
596 | }; |
||
597 | RegisterImporter (base_importers_table, typeof (int), |
||
598 | typeof (short), importer); |
||
599 | |||
600 | importer = delegate (object input) { |
||
601 | return Convert.ToUInt16 ((int) input); |
||
602 | }; |
||
603 | RegisterImporter (base_importers_table, typeof (int), |
||
604 | typeof (ushort), importer); |
||
605 | |||
606 | importer = delegate (object input) { |
||
607 | return Convert.ToUInt32 ((int) input); |
||
608 | }; |
||
609 | RegisterImporter (base_importers_table, typeof (int), |
||
610 | typeof (uint), importer); |
||
611 | |||
612 | importer = delegate (object input) { |
||
613 | return Convert.ToSingle ((int) input); |
||
614 | }; |
||
615 | RegisterImporter (base_importers_table, typeof (int), |
||
616 | typeof (float), importer); |
||
617 | |||
618 | importer = delegate (object input) { |
||
619 | return Convert.ToDouble ((int) input); |
||
620 | }; |
||
621 | RegisterImporter (base_importers_table, typeof (int), |
||
622 | typeof (double), importer); |
||
623 | |||
624 | importer = delegate (object input) { |
||
625 | return Convert.ToDecimal ((double) input); |
||
626 | }; |
||
627 | RegisterImporter (base_importers_table, typeof (double), |
||
628 | typeof (decimal), importer); |
||
629 | |||
630 | |||
631 | importer = delegate (object input) { |
||
632 | return Convert.ToUInt32 ((long) input); |
||
633 | }; |
||
634 | RegisterImporter (base_importers_table, typeof (long), |
||
635 | typeof (uint), importer); |
||
636 | |||
637 | importer = delegate (object input) { |
||
638 | return Convert.ToChar ((string) input); |
||
639 | }; |
||
640 | RegisterImporter (base_importers_table, typeof (string), |
||
641 | typeof (char), importer); |
||
642 | |||
643 | importer = delegate (object input) { |
||
644 | return Convert.ToDateTime ((string) input, datetime_format); |
||
645 | }; |
||
646 | RegisterImporter (base_importers_table, typeof (string), |
||
647 | typeof (DateTime), importer); |
||
648 | } |
||
649 | |||
650 | private static void RegisterImporter ( |
||
651 | IDictionary<Type, IDictionary<Type, ImporterFunc>> table, |
||
652 | Type json_type, Type value_type, ImporterFunc importer) |
||
653 | { |
||
654 | if (! table.ContainsKey (json_type)) |
||
655 | table.Add (json_type, new Dictionary<Type, ImporterFunc> ()); |
||
656 | |||
657 | table[json_type][value_type] = importer; |
||
658 | } |
||
659 | |||
660 | private static void WriteValue (object obj, JsonWriter writer, |
||
661 | bool writer_is_private, |
||
662 | int depth) |
||
663 | { |
||
664 | if (depth > max_nesting_depth) |
||
665 | throw new JsonException ( |
||
666 | String.Format ("Max allowed object depth reached while " + |
||
667 | "trying to export from type {0}", |
||
668 | obj.GetType ())); |
||
669 | |||
670 | if (obj == null) { |
||
671 | writer.Write (null); |
||
672 | return; |
||
673 | } |
||
674 | |||
675 | if (obj is IJsonWrapper) { |
||
676 | if (writer_is_private) |
||
677 | writer.TextWriter.Write (((IJsonWrapper) obj).ToJson ()); |
||
678 | else |
||
679 | ((IJsonWrapper) obj).ToJson (writer); |
||
680 | |||
681 | return; |
||
682 | } |
||
683 | |||
684 | if (obj is String) { |
||
685 | writer.Write ((string) obj); |
||
686 | return; |
||
687 | } |
||
688 | |||
689 | if (obj is Double) { |
||
690 | writer.Write ((double) obj); |
||
691 | return; |
||
692 | } |
||
693 | |||
694 | if (obj is Int32) { |
||
695 | writer.Write ((int) obj); |
||
696 | return; |
||
697 | } |
||
698 | |||
699 | if (obj is Boolean) { |
||
700 | writer.Write ((bool) obj); |
||
701 | return; |
||
702 | } |
||
703 | |||
704 | if (obj is Int64) { |
||
705 | writer.Write ((long) obj); |
||
706 | return; |
||
707 | } |
||
708 | |||
709 | if (obj is Array) { |
||
710 | writer.WriteArrayStart (); |
||
711 | |||
712 | foreach (object elem in (Array) obj) |
||
713 | WriteValue (elem, writer, writer_is_private, depth + 1); |
||
714 | |||
715 | writer.WriteArrayEnd (); |
||
716 | |||
717 | return; |
||
718 | } |
||
719 | |||
720 | if (obj is IList) { |
||
721 | writer.WriteArrayStart (); |
||
722 | foreach (object elem in (IList) obj) |
||
723 | WriteValue (elem, writer, writer_is_private, depth + 1); |
||
724 | writer.WriteArrayEnd (); |
||
725 | |||
726 | return; |
||
727 | } |
||
728 | |||
729 | if (obj is IDictionary) { |
||
730 | writer.WriteObjectStart (); |
||
731 | foreach (DictionaryEntry entry in (IDictionary) obj) { |
||
732 | writer.WritePropertyName ((string) entry.Key); |
||
733 | WriteValue (entry.Value, writer, writer_is_private, |
||
734 | depth + 1); |
||
735 | } |
||
736 | writer.WriteObjectEnd (); |
||
737 | |||
738 | return; |
||
739 | } |
||
740 | |||
741 | Type obj_type = obj.GetType (); |
||
742 | |||
743 | // See if there's a custom exporter for the object |
||
744 | if (custom_exporters_table.ContainsKey (obj_type)) { |
||
745 | ExporterFunc exporter = custom_exporters_table[obj_type]; |
||
746 | exporter (obj, writer); |
||
747 | |||
748 | return; |
||
749 | } |
||
750 | |||
751 | // If not, maybe there's a base exporter |
||
752 | if (base_exporters_table.ContainsKey (obj_type)) { |
||
753 | ExporterFunc exporter = base_exporters_table[obj_type]; |
||
754 | exporter (obj, writer); |
||
755 | |||
756 | return; |
||
757 | } |
||
758 | |||
759 | // Last option, let's see if it's an enum |
||
760 | if (obj is Enum) { |
||
761 | Type e_type = Enum.GetUnderlyingType (obj_type); |
||
762 | |||
763 | if (e_type == typeof (long) |
||
764 | || e_type == typeof (uint) |
||
765 | || e_type == typeof (ulong)) |
||
766 | writer.Write ((ulong) obj); |
||
767 | else |
||
768 | writer.Write ((int) obj); |
||
769 | |||
770 | return; |
||
771 | } |
||
772 | |||
773 | // Okay, so it looks like the input should be exported as an |
||
774 | // object |
||
775 | AddTypeProperties (obj_type); |
||
776 | IList<PropertyMetadata> props = type_properties[obj_type]; |
||
777 | |||
778 | writer.WriteObjectStart (); |
||
779 | foreach (PropertyMetadata p_data in props) { |
||
780 | if (p_data.IsField) { |
||
781 | writer.WritePropertyName (p_data.Info.Name); |
||
782 | WriteValue (((FieldInfo) p_data.Info).GetValue (obj), |
||
783 | writer, writer_is_private, depth + 1); |
||
784 | } |
||
785 | else { |
||
786 | PropertyInfo p_info = (PropertyInfo) p_data.Info; |
||
787 | |||
788 | if (p_info.CanRead) { |
||
789 | writer.WritePropertyName (p_data.Info.Name); |
||
790 | WriteValue (p_info.GetValue (obj, null), |
||
791 | writer, writer_is_private, depth + 1); |
||
792 | } |
||
793 | } |
||
794 | } |
||
795 | writer.WriteObjectEnd (); |
||
796 | } |
||
797 | #endregion |
||
798 | |||
799 | |||
800 | public static string ToJson (object obj) |
||
801 | { |
||
802 | lock (static_writer_lock) { |
||
803 | static_writer.Reset (); |
||
804 | |||
805 | WriteValue (obj, static_writer, true, 0); |
||
806 | |||
807 | return static_writer.ToString (); |
||
808 | } |
||
809 | } |
||
810 | |||
811 | public static void ToJson (object obj, JsonWriter writer) |
||
812 | { |
||
813 | WriteValue (obj, writer, false, 0); |
||
814 | } |
||
815 | |||
816 | public static JsonData ToObject (JsonReader reader) |
||
817 | { |
||
818 | return (JsonData) ToWrapper ( |
||
819 | delegate { return new JsonData (); }, reader); |
||
820 | } |
||
821 | |||
822 | public static JsonData ToObject (TextReader reader) |
||
823 | { |
||
824 | JsonReader json_reader = new JsonReader (reader); |
||
825 | |||
826 | return (JsonData) ToWrapper ( |
||
827 | delegate { return new JsonData (); }, json_reader); |
||
828 | } |
||
829 | |||
830 | public static JsonData ToObject (string json) |
||
831 | { |
||
832 | return (JsonData) ToWrapper ( |
||
833 | delegate { return new JsonData (); }, json); |
||
834 | } |
||
835 | |||
836 | public static T ToObject<T> (JsonReader reader) |
||
837 | { |
||
838 | return (T) ReadValue (typeof (T), reader); |
||
839 | } |
||
840 | |||
841 | public static T ToObject<T> (TextReader reader) |
||
842 | { |
||
843 | JsonReader json_reader = new JsonReader (reader); |
||
844 | |||
845 | return (T) ReadValue (typeof (T), json_reader); |
||
846 | } |
||
847 | |||
848 | public static T ToObject<T> (string json) |
||
849 | { |
||
850 | JsonReader reader = new JsonReader (json); |
||
851 | |||
852 | return (T) ReadValue (typeof (T), reader); |
||
853 | } |
||
854 | |||
855 | public static IJsonWrapper ToWrapper (WrapperFactory factory, |
||
856 | JsonReader reader) |
||
857 | { |
||
858 | return ReadValue (factory, reader); |
||
859 | } |
||
860 | |||
861 | public static IJsonWrapper ToWrapper (WrapperFactory factory, |
||
862 | string json) |
||
863 | { |
||
864 | JsonReader reader = new JsonReader (json); |
||
865 | |||
866 | return ReadValue (factory, reader); |
||
867 | } |
||
868 | |||
869 | public static void RegisterExporter<T> (ExporterFunc<T> exporter) |
||
870 | { |
||
871 | ExporterFunc exporter_wrapper = |
||
872 | delegate (object obj, JsonWriter writer) { |
||
873 | exporter ((T) obj, writer); |
||
874 | }; |
||
875 | |||
876 | custom_exporters_table[typeof (T)] = exporter_wrapper; |
||
877 | } |
||
878 | |||
879 | public static void RegisterImporter<TJson, TValue> ( |
||
880 | ImporterFunc<TJson, TValue> importer) |
||
881 | { |
||
882 | ImporterFunc importer_wrapper = |
||
883 | delegate (object input) { |
||
884 | return importer ((TJson) input); |
||
885 | }; |
||
886 | |||
887 | RegisterImporter (custom_importers_table, typeof (TJson), |
||
888 | typeof (TValue), importer_wrapper); |
||
889 | } |
||
890 | |||
891 | public static void UnregisterExporters () |
||
892 | { |
||
893 | custom_exporters_table.Clear (); |
||
894 | } |
||
895 | |||
896 | public static void UnregisterImporters () |
||
897 | { |
||
898 | custom_importers_table.Clear (); |
||
899 | } |
||
900 | } |
||
901 | } |