Source

Manatee.Json / Json / Serialization / JsonSerializationTypeRegistry.cs

Full commit
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/***************************************************************************************

	Copyright 2012 Greg Dennis

	   Licensed under the Apache License, Version 2.0 (the "License");
	   you may not use this file except in compliance with the License.
	   You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

	   Unless required by applicable law or agreed to in writing, software
	   distributed under the License is distributed on an "AS IS" BASIS,
	   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	   See the License for the specific language governing permissions and
	   limitations under the License.
 
	File Name:		JsonSerializationTypeRegistry.cs
	Namespace:		Manatee.Json.Serialization
	Class Name:		JsonSerializationTypeRegistry
	Purpose:		Manages methods for serializing object types which do not
					implement IJsonCompatible and cannot be automatically serialized.

***************************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using Manatee.Json.Enumerations;
using Manatee.Json.Exceptions;
using Manatee.Json.Helpers;

namespace Manatee.Json.Serialization
{
	/// <summary>
	/// Manages methods for serializing object types which do not implement IJsonCompatible and
	/// cannot be automatically serialized.
	/// </summary>
	public static class JsonSerializationTypeRegistry
	{
		#region Operational Items
		/// <summary>
		/// Declares the required signature for a serializer method.
		/// </summary>
		/// <typeparam name="T">The type which the method serializes.</typeparam>
		/// <param name="input">The object to be serialized.</param>
		/// <returns>The JSON representation of the object.</returns>
		public delegate JsonValue ToJsonDelegate<T>(T input);
		/// <summary>
		/// Declares the required signature for a deserializer method.
		/// </summary>
		/// <typeparam name="T">The type which the method deserializes.</typeparam>
		/// <param name="json">The JSON representation of the object.</param>
		/// <returns>The deserialized object.</returns>
		public delegate T FromJsonDelegate<T>(JsonValue json);

		private static readonly Dictionary<Type, Delegate> ToJsonConverters;
		private static readonly Dictionary<Type, Delegate> FromJsonConverters;
		private static readonly JsonSerializer _serializer;
		private static readonly object lockHolder = new object();

		private static JsonSerializerOptions requestedOptions;

		static JsonSerializationTypeRegistry()
		{
			ToJsonConverters = new Dictionary<Type, Delegate>();
			FromJsonConverters = new Dictionary<Type, Delegate>();
			_serializer = new JsonSerializer();
			RegisterLocalTypes();
		}

		internal static bool TryEncode<T>(this JsonSerializer serializer, T obj, out JsonValue json)
		{
			var converter = GetToJsonConverter<T>();
			if (converter == null)
			{
				json = null;
				return false;
			}
			lock (lockHolder)
			{
				requestedOptions = serializer.Options;
				json = converter(obj);
				requestedOptions = null;
				return true;
			}
		}
		internal static bool TryDecode<T>(this JsonSerializer serializer, JsonValue json, out T obj)
		{
			var converter = GetFromJsonConverter<T>();
			if (converter == null)
			{
				obj = default(T);
				return false;
			}
			lock (lockHolder)
			{
				requestedOptions = serializer.Options;
				obj = converter(json);
				requestedOptions = null;
				return true;
			}
		}

		private static void RegisterLocalTypes()
		{
			RegisterType(EncodeDateTime, DecodeDateTime);
			RegisterType(EncodeTimeSpan, DecodeTimeSpan);
			RegisterType(EncodeGuid, DecodeGuid);
		}
		private static ToJsonDelegate<T> GetToJsonConverter<T>()
		{
			var type = typeof (T);
			return ToJsonConverters.ContainsKey(type) ? (ToJsonDelegate<T>)ToJsonConverters[type] : null;
		}
		private static FromJsonDelegate<T> GetFromJsonConverter<T>()
		{
			var type = typeof(T);
			return FromJsonConverters.ContainsKey(type) ? (FromJsonDelegate<T>)FromJsonConverters[type] : null;
		}
		#endregion

		#region Registration
		/// <summary>
		/// Registers an encode/decode method pair for a specific type.
		/// </summary>
		/// <typeparam name="T">The type.</typeparam>
		/// <param name="toJson">The serializer method.</param>
		/// <param name="fromJson">The deserializer method.</param>
		public static void RegisterType<T>(ToJsonDelegate<T> toJson, FromJsonDelegate<T> fromJson)
		{
			if (((toJson == null) && (fromJson != null)) ||
				((toJson != null) && (fromJson == null)))
				throw new TypeRegistrationException(typeof (T));
			var type = typeof (T);
			ToJsonConverters[type] = toJson;
			FromJsonConverters[type] = fromJson;
		}
		/// <summary>
		/// Registers an encode/decode method pair for a nullable type.
		/// </summary>
		/// <typeparam name="T">The underlying type of the nullable.</typeparam>
		/// <remarks>To register a nullable int, the call should be RegisterNullableType&lt;int&gt;()</remarks>
		public static void RegisterNullableType<T>() where T : struct
		{
			var type = typeof(T);
			var nullableType = typeof(T?);
			var toJson = typeof(JsonSerializationTypeRegistry).GetMethod("EncodeNullable").MakeGenericMethod(type);
			var fromJson = typeof(JsonSerializationTypeRegistry).GetMethod("DecodeNullable").MakeGenericMethod(type);
			ToJsonConverters[nullableType] = Delegate.CreateDelegate(typeof(ToJsonDelegate<T?>), toJson);
			FromJsonConverters[nullableType] = Delegate.CreateDelegate(typeof(FromJsonDelegate<T?>), fromJson);
		}
		/// <summary>
		/// Registers an encode/decode method pair for a typed list.
		/// </summary>
		/// <typeparam name="T">The underlying type of the list.</typeparam>
		/// <remarks>To register a list of ints, the call should be RegisterListType&lt;int&gt;()</remarks>
		public static void RegisterListType<T>()
		{
			var type = typeof(T);
			var listType = typeof (List<T>);
			var toJson = typeof(JsonSerializationTypeRegistry).GetMethod("EncodeGenericList").MakeGenericMethod(type);
			var fromJson = typeof(JsonSerializationTypeRegistry).GetMethod("DecodeGenericList").MakeGenericMethod(type);
			ToJsonConverters[listType] = Delegate.CreateDelegate(typeof (ToJsonDelegate<List<T>>), toJson);
			FromJsonConverters[listType] = Delegate.CreateDelegate(typeof (FromJsonDelegate<List<T>>), fromJson);
		}
		/// <summary>
		/// Registers an encode/decode method pair for a typed dictionary.
		/// </summary>
		/// <typeparam name="TKey">The underlying type used as the key for the dictionary.</typeparam>
		/// <typeparam name="TValue">The underlying type used as the value for the dictionary.</typeparam>
		/// <remarks>To register a dictionary of ints keyed by strings, the call should be RegisterDictionaryType&lt;string, int&gt;()</remarks>
		public static void RegisterDictionaryType<TKey, TValue>()
		{
			var keyType = typeof(TKey);
			var valueType = typeof (TValue);
			var dictType = typeof(Dictionary<TKey, TValue>);
			var toJson = typeof(JsonSerializationTypeRegistry).GetMethod("EncodeGenericDictionary").MakeGenericMethod(keyType, valueType);
			var fromJson = typeof(JsonSerializationTypeRegistry).GetMethod("DecodeGenericDictionary").MakeGenericMethod(keyType, valueType);
			ToJsonConverters[dictType] = Delegate.CreateDelegate(typeof(ToJsonDelegate<Dictionary<TKey, TValue>>), toJson);
			FromJsonConverters[dictType] = Delegate.CreateDelegate(typeof(FromJsonDelegate<Dictionary<TKey, TValue>>), fromJson);
		}
		/// <summary>
		/// Registers an encode/decode method pair for a typed queue.
		/// </summary>
		/// <typeparam name="T">The underlying type of the queue.</typeparam>
		/// <remarks>To register a queue of ints, the call should be RegisterQueueType&lt;int&gt;()</remarks>
		public static void RegisterQueueType<T>()
		{
			var type = typeof(T);
			var queueType = typeof(Queue<T>);
			var toJson = typeof(JsonSerializationTypeRegistry).GetMethod("EncodeGenericQueue").MakeGenericMethod(type);
			var fromJson = typeof(JsonSerializationTypeRegistry).GetMethod("DecodeGenericQueue").MakeGenericMethod(type);
			ToJsonConverters[queueType] = Delegate.CreateDelegate(typeof(ToJsonDelegate<Queue<T>>), toJson);
			FromJsonConverters[queueType] = Delegate.CreateDelegate(typeof(FromJsonDelegate<Queue<T>>), fromJson);
		}
		/// <summary>
		/// Registers an encode/decode method pair for a typed stack.
		/// </summary>
		/// <typeparam name="T">The underlying type of the stack.</typeparam>
		/// <remarks>To register a stack of ints, the call should be RegisterStackType&lt;int&gt;()</remarks>
		public static void RegisterStackType<T>()
		{
			var type = typeof(T);
			var stackType = typeof(Stack<T>);
			var toJson = typeof(JsonSerializationTypeRegistry).GetMethod("EncodeGenericStack").MakeGenericMethod(type);
			var fromJson = typeof(JsonSerializationTypeRegistry).GetMethod("DecodeGenericStack").MakeGenericMethod(type);
			ToJsonConverters[stackType] = Delegate.CreateDelegate(typeof(ToJsonDelegate<Stack<T>>), toJson);
			FromJsonConverters[stackType] = Delegate.CreateDelegate(typeof(FromJsonDelegate<Stack<T>>), fromJson);
		}
		#endregion

		#region Specified Types
		#region DateTime
		/// <summary>
		/// Encodes a DateTime object to its JSON representation.
		/// </summary>
		/// <param name="dt">A DateTime object.</param>
		/// <returns>The JSON representation of the DateTime.</returns>
		public static JsonValue EncodeDateTime(DateTime dt)
		{
			if (requestedOptions == null)
				return dt.ToString();
			switch (requestedOptions.DateTimeSerializationFormat)
			{
				case DateTimeSerializationFormat.JavaConstructor:
					return string.Format("/Date({0})/", dt.Ticks/TimeSpan.TicksPerMillisecond);
				case DateTimeSerializationFormat.Milliseconds:
					return dt.Ticks/TimeSpan.TicksPerMillisecond;
				default:
					return dt.ToString("s");
			}
		}
		/// <summary>
		/// Decodes a DateTime object from its JSON representation.
		/// </summary>
		/// <param name="json">A JSON representation of a DateTime.</param>
		/// <returns>The DateTime object.</returns>
		public static DateTime DecodeDateTime(JsonValue json)
		{
			if (requestedOptions == null)
				return DateTime.Parse(json.String);
			switch (requestedOptions.DateTimeSerializationFormat)
			{
				case DateTimeSerializationFormat.JavaConstructor:
					return new DateTime(long.Parse(json.String.Substring(6, json.String.Length - 8))*TimeSpan.TicksPerMillisecond);
				case DateTimeSerializationFormat.Milliseconds:
					return new DateTime((long) json.Number*TimeSpan.TicksPerMillisecond);
				default:
					return DateTime.Parse(json.String);
			}
		}
		#endregion
		#region TimeSpan
		/// <summary>
		/// Encodes a TimeSpan object to its JSON representation.
		/// </summary>
		/// <param name="ts">A TimeSpan object.</param>
		/// <returns>The JSON representation of the TimeSpan.</returns>
		public static JsonValue EncodeTimeSpan(TimeSpan ts)
		{
			return ts.ToString();
		}
		/// <summary>
		/// Decodes a TimeSpan object from its JSON representation.
		/// </summary>
		/// <param name="json">A JSON representation of a TimeSpan.</param>
		/// <returns>The TimeSpan object.</returns>
		public static TimeSpan DecodeTimeSpan(JsonValue json)
		{
			return json.Type == JsonValueType.String ? TimeSpan.Parse(json.String) : default(TimeSpan);
		}
		#endregion
		#region Guid
		/// <summary>
		/// Encodes a Guid object to its JSON representation.
		/// </summary>
		/// <param name="guid">A Guid object.</param>
		/// <returns>The JSON representation of the Guid.</returns>
		public static JsonValue EncodeGuid(Guid guid)
		{
			return guid.ToString();
		}
		/// <summary>
		/// Decodes a Guid object from its JSON representation.
		/// </summary>
		/// <param name="json">A JSON representation of a Guid.</param>
		/// <returns>The Guid object.</returns>
		public static Guid DecodeGuid(JsonValue json)
		{
			return json.Type == JsonValueType.String ? Guid.Parse(json.String) : default(Guid);
		}
		#endregion
		#endregion

		#region Generic Types
		#region Nullable<T>
		/// <summary>
		/// Encodes a Nullable&lt;T&gt; to its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the nullable value.</typeparam>
		/// <param name="nullable">The Nullable&lt;T&gt; object.</param>
		/// <returns>The JSON representation of the Nullable&lt;T&gt;.</returns>
		public static JsonValue EncodeNullable<T>(T? nullable) where T : struct
		{
			return nullable.HasValue ? _serializer.Serialize(nullable.Value) : JsonValue.Null;
		}
		/// <summary>
		/// Decodes a Nullable&lt;T&gt; object from its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the nullable value.</typeparam>
		/// <param name="json">A JSON representation of a Nullable&lt;T&gt;.</param>
		/// <returns>The Nullable&lt;T&gt; object.</returns>
		public static T? DecodeNullable<T>(JsonValue json) where T : struct
		{
			if (json == JsonValue.Null) return null;
			T? nullable = _serializer.Deserialize<T>(json);
			return nullable;
		}
		#endregion
		#region List<T>
		/// <summary>
		/// Encodes a List&lt;T&gt; to its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the list.</typeparam>
		/// <param name="list">The List&lt;T&gt; object.</param>
		/// <returns>The JSON representation of the List&lt;T&gt;.</returns>
		public static JsonValue EncodeGenericList<T>(List<T> list)
		{
			var array = new JsonArray();
			array.AddRange(list.Select(item => _serializer.Serialize(item)));
			return array;
		}
		/// <summary>
		/// Decodes a List&lt;T&gt; object from its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the list.</typeparam>
		/// <param name="json">A JSON representation of a List&lt;T&gt;.</param>
		/// <returns>The List&lt;T&gt; object.</returns>
		public static List<T> DecodeGenericList<T>(JsonValue json)
		{
			var list = new List<T>();
			list.AddRange(json.Array.Select(jv => _serializer.Deserialize<T>(jv)));
			return list;
		}
		#endregion
		#region Dictionary<TKey, TValue>
		/// <summary>
		/// Encodes a Dictionary&lt;T&gt; to its JSON representation.
		/// </summary>
		/// <typeparam name="TKey">The underlying type used as the key for the dictionary.</typeparam>
		/// <typeparam name="TValue">The underlying type used as the value for the dictionary.</typeparam>
		/// <param name="dict">The Dictionary&lt;T&gt; object.</param>
		/// <returns>The JSON representation of the Dictionary&lt;T&gt;.</returns>
		public static JsonValue EncodeGenericDictionary<TKey, TValue>(Dictionary<TKey, TValue> dict)
		{
			var array = new JsonArray();
			array.AddRange(dict.Select(item => (JsonValue)(new JsonObject
			                                               	{
			                                               		{"Key", _serializer.Serialize(item.Key)},
																{"Value", _serializer.Serialize(item.Value)}
			                                               	})));
			return array;
		}
		/// <summary>
		/// Decodes a Dictionary&lt;T&gt; object from its JSON representation.
		/// </summary>
		/// <typeparam name="TKey">The underlying type used as the key for the dictionary.</typeparam>
		/// <typeparam name="TValue">The underlying type used as the value for the dictionary.</typeparam>
		/// <param name="json">A JSON representation of a Dictionary&lt;T&gt;.</param>
		/// <returns>The Dictionary&lt;T&gt; object.</returns>
		public static Dictionary<TKey, TValue> DecodeGenericDictionary<TKey, TValue>(JsonValue json)
		{
			var dict = new Dictionary<TKey, TValue>();
			foreach (var jv in json.Array)
			{
				dict.Add(_serializer.Deserialize<TKey>(jv.Object["Key"]),
						 _serializer.Deserialize<TValue>(jv.Object["Value"]));
			}
			return dict;
		}
		#endregion
		#region Queue<T>
		/// <summary>
		/// Encodes a Queue&lt;T&gt; to its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the queue.</typeparam>
		/// <param name="queue">The Queue&lt;T&gt; object.</param>
		/// <returns>The JSON representation of the Queue&lt;T&gt;.</returns>
		public static JsonValue EncodeGenericQueue<T>(Queue<T> queue)
		{
			var array = new JsonArray();
			for (int i = 0; i < queue.Count; i++)
			{
				array.Add(_serializer.Serialize(queue.ElementAt(i)));
			}
			return array;
		}
		/// <summary>
		/// Decodes a Queue&lt;T&gt; object from its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the queue.</typeparam>
		/// <param name="json">A JSON representation of a Queue&lt;T&gt;.</param>
		/// <returns>The Queue&lt;T&gt; object.</returns>
		public static Queue<T> DecodeGenericQueue<T>(JsonValue json)
		{
			var queue = new Queue<T>();
			for (int i = 0; i < json.Array.Count; i++)
			{
				queue.Enqueue(_serializer.Deserialize<T>(json.Array[i]));
			}
			return queue;
		}
		#endregion
		#region Stack<T>
		/// <summary>
		/// Encodes a Stack&lt;T&gt; to its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the stack.</typeparam>
		/// <param name="stack">The Stack&lt;T&gt; object.</param>
		/// <returns>The JSON representation of the Stack&lt;T&gt;.</returns>
		public static JsonValue EncodeGenericStack<T>(Stack<T> stack)
		{
			var array = new JsonArray();
			for (int i = 0; i < stack.Count; i++)
			{
				array.Add(_serializer.Serialize(stack.ElementAt(i)));
			}
			return array;
		}
		/// <summary>
		/// Decodes a Stack&lt;T&gt; object from its JSON representation.
		/// </summary>
		/// <typeparam name="T">The underlying type of the stack.</typeparam>
		/// <param name="json">A JSON representation of a Stack&lt;T&gt;.</param>
		/// <returns>The Stack&lt;T&gt; object.</returns>
		public static Stack<T> DecodeGenericStack<T>(JsonValue json)
		{
			var stack = new Stack<T>();
			for (int i = 0; i < json.Array.Count; i++)
			{
				stack.Push(_serializer.Deserialize<T>(json.Array[i]));
			}
			return stack;
		}
		#endregion
		#endregion
	}
}