PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ServiceStack.Text/JsConfig.cs

https://github.com/jsauve/ServiceStack.Text
C# | 448 lines | 334 code | 56 blank | 58 comment | 37 complexity | efd9a92f93ca45432e574f0c23ad8032 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using ServiceStack.Text.Common;
  5. using ServiceStack.Text.Json;
  6. using ServiceStack.Text.Jsv;
  7. #if WINDOWS_PHONE
  8. using ServiceStack.Text.WP;
  9. #endif
  10. namespace ServiceStack.Text
  11. {
  12. public static class
  13. JsConfig
  14. {
  15. static JsConfig()
  16. {
  17. //In-built default serialization, to Deserialize Color struct do:
  18. //JsConfig<System.Drawing.Color>.SerializeFn = c => c.ToString().Replace("Color ", "").Replace("[", "").Replace("]", "");
  19. //JsConfig<System.Drawing.Color>.DeSerializeFn = System.Drawing.Color.FromName;
  20. Reset();
  21. }
  22. [ThreadStatic]
  23. private static bool? tsConvertObjectTypesIntoStringDictionary;
  24. private static bool? sConvertObjectTypesIntoStringDictionary;
  25. public static bool ConvertObjectTypesIntoStringDictionary
  26. {
  27. get
  28. {
  29. return tsConvertObjectTypesIntoStringDictionary ?? sConvertObjectTypesIntoStringDictionary ?? false;
  30. }
  31. set
  32. {
  33. tsConvertObjectTypesIntoStringDictionary = value;
  34. if (!sConvertObjectTypesIntoStringDictionary.HasValue) sConvertObjectTypesIntoStringDictionary = value;
  35. }
  36. }
  37. [ThreadStatic]
  38. private static bool? tsIncludeNullValues;
  39. private static bool? sIncludeNullValues;
  40. public static bool IncludeNullValues
  41. {
  42. get
  43. {
  44. return tsIncludeNullValues ?? sIncludeNullValues ?? false;
  45. }
  46. set
  47. {
  48. tsIncludeNullValues = value;
  49. if (!sIncludeNullValues.HasValue) sIncludeNullValues = value;
  50. }
  51. }
  52. [ThreadStatic]
  53. private static bool? tsExcludeTypeInfo;
  54. private static bool? sExcludeTypeInfo;
  55. public static bool ExcludeTypeInfo
  56. {
  57. get
  58. {
  59. return tsExcludeTypeInfo ?? sExcludeTypeInfo ?? false;
  60. }
  61. set
  62. {
  63. tsExcludeTypeInfo = value;
  64. if (!sExcludeTypeInfo.HasValue) sExcludeTypeInfo = value;
  65. }
  66. }
  67. [ThreadStatic]
  68. private static JsonDateHandler? tsDateHandler;
  69. private static JsonDateHandler? sDateHandler;
  70. public static JsonDateHandler DateHandler
  71. {
  72. get
  73. {
  74. return tsDateHandler ?? sDateHandler ?? JsonDateHandler.TimestampOffset;
  75. }
  76. set
  77. {
  78. tsDateHandler = value;
  79. if (!sDateHandler.HasValue) sDateHandler = value;
  80. }
  81. }
  82. /// <summary>
  83. /// Sets which format to use when serializing TimeSpans
  84. /// </summary>
  85. public static JsonTimeSpanHandler TimeSpanHandler { get; set; }
  86. /// <summary>
  87. /// <see langword="true"/> if the <see cref="ITypeSerializer"/> is configured
  88. /// to take advantage of <see cref="CLSCompliantAttribute"/> specification,
  89. /// to support user-friendly serialized formats, ie emitting camelCasing for JSON
  90. /// and parsing member names and enum values in a case-insensitive manner.
  91. /// </summary>
  92. [ThreadStatic]
  93. private static bool? tsEmitCamelCaseNames;
  94. private static bool? sEmitCamelCaseNames;
  95. public static bool EmitCamelCaseNames
  96. {
  97. // obeying the use of ThreadStatic, but allowing for setting JsConfig once as is the normal case
  98. get
  99. {
  100. return tsEmitCamelCaseNames ?? sEmitCamelCaseNames ?? false;
  101. }
  102. set
  103. {
  104. tsEmitCamelCaseNames = value;
  105. if (!sEmitCamelCaseNames.HasValue) sEmitCamelCaseNames = value;
  106. }
  107. }
  108. /// <summary>
  109. /// Gets or sets a value indicating if the framework should throw serialization exceptions
  110. /// or continue regardless of deserialization errors. If <see langword="true"/> the framework
  111. /// will throw; otherwise, it will parse as many fields as possible. The default is <see langword="false"/>.
  112. /// </summary>
  113. [ThreadStatic]
  114. private static bool? tsThrowOnDeserializationError;
  115. private static bool? sThrowOnDeserializationError;
  116. public static bool ThrowOnDeserializationError
  117. {
  118. // obeying the use of ThreadStatic, but allowing for setting JsConfig once as is the normal case
  119. get
  120. {
  121. return tsThrowOnDeserializationError ?? sThrowOnDeserializationError ?? false;
  122. }
  123. set
  124. {
  125. tsThrowOnDeserializationError = value;
  126. if (!sThrowOnDeserializationError.HasValue) sThrowOnDeserializationError = value;
  127. }
  128. }
  129. internal static HashSet<Type> HasSerializeFn = new HashSet<Type>();
  130. internal static HashSet<Type> TreatValueAsRefTypes = new HashSet<Type>();
  131. internal static bool TreatAsRefType(Type valueType)
  132. {
  133. return TreatValueAsRefTypes.Contains(valueType.IsGenericType ? valueType.GetGenericTypeDefinition() : valueType);
  134. }
  135. public static void Reset()
  136. {
  137. tsConvertObjectTypesIntoStringDictionary = sConvertObjectTypesIntoStringDictionary = null;
  138. tsIncludeNullValues = sIncludeNullValues = null;
  139. tsExcludeTypeInfo = sExcludeTypeInfo = null;
  140. tsEmitCamelCaseNames = sEmitCamelCaseNames = null;
  141. tsDateHandler = sDateHandler = null;
  142. tsThrowOnDeserializationError = sThrowOnDeserializationError = null;
  143. HasSerializeFn = new HashSet<Type>();
  144. TreatValueAsRefTypes = new HashSet<Type> {
  145. typeof(KeyValuePair<,>)
  146. };
  147. }
  148. #if MONOTOUCH
  149. /// <summary>
  150. /// Provide hint to MonoTouch AOT compiler to pre-compile generic classes for all your DTOs.
  151. /// Just needs to be called once in a static constructor.
  152. /// </summary>
  153. [MonoTouch.Foundation.Preserve]
  154. public static void InitForAot() { }
  155. [MonoTouch.Foundation.Preserve]
  156. public static void RegisterForAot()
  157. {
  158. JsonAotConfig.Register<Poco>();
  159. RegisterElement<Poco, string>();
  160. RegisterElement<Poco, bool>();
  161. RegisterElement<Poco, char>();
  162. RegisterElement<Poco, byte>();
  163. RegisterElement<Poco, sbyte>();
  164. RegisterElement<Poco, short>();
  165. RegisterElement<Poco, ushort>();
  166. RegisterElement<Poco, int>();
  167. RegisterElement<Poco, uint>();
  168. RegisterElement<Poco, long>();
  169. RegisterElement<Poco, ulong>();
  170. RegisterElement<Poco, float>();
  171. RegisterElement<Poco, double>();
  172. RegisterElement<Poco, decimal>();
  173. RegisterElement<Poco, Guid>();
  174. RegisterElement<Poco, DateTime>();
  175. RegisterElement<Poco, TimeSpan>();
  176. RegisterElement<Poco, bool?>();
  177. RegisterElement<Poco, char?>();
  178. RegisterElement<Poco, byte?>();
  179. RegisterElement<Poco, sbyte?>();
  180. RegisterElement<Poco, short?>();
  181. RegisterElement<Poco, ushort?>();
  182. RegisterElement<Poco, int?>();
  183. RegisterElement<Poco, uint?>();
  184. RegisterElement<Poco, long?>();
  185. RegisterElement<Poco, ulong?>();
  186. RegisterElement<Poco, float?>();
  187. RegisterElement<Poco, double?>();
  188. RegisterElement<Poco, decimal?>();
  189. RegisterElement<Poco, Guid?>();
  190. RegisterElement<Poco, DateTime?>();
  191. RegisterElement<Poco, TimeSpan?>();
  192. RegisterQueryStringWriter();
  193. RegisterCsvSerializer();
  194. }
  195. [MonoTouch.Foundation.Preserve]
  196. public static bool RegisterTypeForAot<T>()
  197. {
  198. bool ret = false;
  199. try
  200. {
  201. JsonAotConfig.Register<T>();
  202. int i = 0;
  203. if(JsvWriter<T>.WriteFn() != null && JsvReader<T>.GetParseFn() != null) i++;
  204. if(JsonWriter<T>.WriteFn() != null && JsonReader<T>.GetParseFn() != null) i++;
  205. if(QueryStringWriter<Poco>.WriteFn() != null) i++;
  206. CsvSerializer<T>.WriteFn();
  207. CsvSerializer<T>.WriteObject(null, null);
  208. CsvWriter<T>.WriteObject(null, null);
  209. CsvWriter<T>.WriteObjectRow(null, null);
  210. ret = true;
  211. }catch(Exception){}
  212. return ret;
  213. }
  214. [MonoTouch.Foundation.Preserve]
  215. static void RegisterQueryStringWriter()
  216. {
  217. var i = 0;
  218. if (QueryStringWriter<Poco>.WriteFn() != null) i++;
  219. }
  220. [MonoTouch.Foundation.Preserve]
  221. static void RegisterCsvSerializer()
  222. {
  223. CsvSerializer<Poco>.WriteFn();
  224. CsvSerializer<Poco>.WriteObject(null, null);
  225. CsvWriter<Poco>.WriteObject(null, null);
  226. CsvWriter<Poco>.WriteObjectRow(null, null);
  227. }
  228. [MonoTouch.Foundation.Preserve]
  229. public static void RegisterElement<T, TElement>()
  230. {
  231. JsonAotConfig.RegisterElement<T, TElement>();
  232. }
  233. #endif
  234. }
  235. #if MONOTOUCH
  236. [MonoTouch.Foundation.Preserve(AllMembers=true)]
  237. internal class Poco
  238. {
  239. public string Dummy { get; set; }
  240. }
  241. [MonoTouch.Foundation.Preserve(AllMembers=true)]
  242. internal class JsonAotConfig
  243. {
  244. static JsReader<JsonTypeSerializer> reader;
  245. static JsWriter<JsonTypeSerializer> writer;
  246. static JsonTypeSerializer serializer;
  247. static JsonAotConfig()
  248. {
  249. serializer = new JsonTypeSerializer();
  250. reader = new JsReader<JsonTypeSerializer>();
  251. writer = new JsWriter<JsonTypeSerializer>();
  252. }
  253. public static ParseStringDelegate GetParseFn(Type type)
  254. {
  255. var parseFn = JsonTypeSerializer.Instance.GetParseFn(type);
  256. return parseFn;
  257. }
  258. internal static ParseStringDelegate RegisterBuiltin<T>()
  259. {
  260. var i = 0;
  261. if (reader.GetParseFn<T>() != null) i++;
  262. if (JsonReader<T>.GetParseFn() != null) i++;
  263. if (JsonReader<T>.Parse(null) != null) i++;
  264. if (JsonWriter<T>.WriteFn() != null) i++;
  265. return serializer.GetParseFn<T>();
  266. }
  267. public static void Register<T>()
  268. {
  269. var i = 0;
  270. var serializer = JsonTypeSerializer.Instance;
  271. if (new List<T>() != null) i++;
  272. if (new T[0] != null) i++;
  273. if (serializer.GetParseFn<T>() != null) i++;
  274. if (DeserializeArray<T[], JsonTypeSerializer>.Parse != null) i++;
  275. JsConfig<T>.ExcludeTypeInfo = false;
  276. //JsConfig<T>.SerializeFn = arg => "";
  277. //JsConfig<T>.DeSerializeFn = arg => default(T);
  278. DeserializeArrayWithElements<T, JsonTypeSerializer>.ParseGenericArray(null, null);
  279. DeserializeCollection<JsonTypeSerializer>.ParseCollection<T>(null, null, null);
  280. DeserializeListWithElements<T, JsonTypeSerializer>.ParseGenericList(null, null, null);
  281. SpecializedQueueElements<T>.ConvertToQueue(null);
  282. SpecializedQueueElements<T>.ConvertToStack(null);
  283. WriteListsOfElements<T, JsonTypeSerializer>.WriteList(null, null);
  284. WriteListsOfElements<T, JsonTypeSerializer>.WriteIList(null, null);
  285. WriteListsOfElements<T, JsonTypeSerializer>.WriteEnumerable(null, null);
  286. WriteListsOfElements<T, JsonTypeSerializer>.WriteListValueType(null, null);
  287. WriteListsOfElements<T, JsonTypeSerializer>.WriteIListValueType(null, null);
  288. JsonReader<T>.Parse(null);
  289. JsonWriter<T>.WriteFn();
  290. TranslateListWithElements<T>.LateBoundTranslateToGenericICollection(null, null);
  291. TranslateListWithConvertibleElements<T, T>.LateBoundTranslateToGenericICollection(null, null);
  292. QueryStringWriter<T>.WriteObject(null, null);
  293. }
  294. // Edited to fix issues with null List<Guid> properties in response objects
  295. public static void RegisterElement<T, TElement>()
  296. {
  297. RegisterBuiltin<TElement>();
  298. DeserializeDictionary<JsonTypeSerializer>.ParseDictionary<T, TElement>(null, null, null, null);
  299. DeserializeDictionary<JsonTypeSerializer>.ParseDictionary<TElement, T>(null, null, null, null);
  300. ToStringDictionaryMethods<T, TElement, JsonTypeSerializer>.WriteIDictionary(null, null, null, null);
  301. ToStringDictionaryMethods<TElement, T, JsonTypeSerializer>.WriteIDictionary(null, null, null, null);
  302. // Include List deserialisations from the Register<> method above. This solves issue where List<Guid> properties on responses deserialise to null.
  303. // No idea why this is happening because there is no visible exception raised. Suspect MonoTouch is swallowing an AOT exception somewhere.
  304. DeserializeArrayWithElements<TElement, JsonTypeSerializer>.ParseGenericArray(null, null);
  305. DeserializeListWithElements<TElement, JsonTypeSerializer>.ParseGenericList(null, null, null);
  306. // Cannot use the line below for some unknown reason - when trying to compile to run on device, mtouch bombs during native code compile.
  307. // Something about this line or its inner workings is offensive to mtouch. Luckily this was not needed for my List<Guide> issue.
  308. // DeserializeCollection<JsonTypeSerializer>.ParseCollection<TElement>(null, null, null);
  309. TranslateListWithElements<TElement>.LateBoundTranslateToGenericICollection(null, typeof(List<TElement>));
  310. TranslateListWithConvertibleElements<TElement, TElement>.LateBoundTranslateToGenericICollection(null, typeof(List<TElement>));
  311. }
  312. }
  313. #endif
  314. public class JsConfig<T> //where T : struct
  315. {
  316. /// <summary>
  317. /// Never emit type info for this type
  318. /// </summary>
  319. public static bool ExcludeTypeInfo = false;
  320. /// <summary>
  321. /// <see langword="true"/> if the <see cref="ITypeSerializer"/> is configured
  322. /// to take advantage of <see cref="CLSCompliantAttribute"/> specification,
  323. /// to support user-friendly serialized formats, ie emitting camelCasing for JSON
  324. /// and parsing member names and enum values in a case-insensitive manner.
  325. /// </summary>
  326. public static bool EmitCamelCaseNames = false;
  327. /// <summary>
  328. /// Define custom serialization fn for BCL Structs
  329. /// </summary>
  330. private static Func<T, string> serializeFn;
  331. public static Func<T, string> SerializeFn
  332. {
  333. get { return serializeFn; }
  334. set
  335. {
  336. serializeFn = value;
  337. if (value != null)
  338. JsConfig.HasSerializeFn.Add(typeof(T));
  339. else
  340. JsConfig.HasSerializeFn.Remove(typeof(T));
  341. }
  342. }
  343. /// <summary>
  344. /// Opt-in flag to set some Value Types to be treated as a Ref Type
  345. /// </summary>
  346. public bool TreatValueAsRefTypes
  347. {
  348. get { return JsConfig.TreatValueAsRefTypes.Contains(typeof (T)); }
  349. set
  350. {
  351. if (value)
  352. JsConfig.TreatValueAsRefTypes.Add(typeof(T));
  353. else
  354. JsConfig.TreatValueAsRefTypes.Remove(typeof(T));
  355. }
  356. }
  357. /// <summary>
  358. /// Define custom deserialization fn for BCL Structs
  359. /// </summary>
  360. public static Func<string, T> DeSerializeFn;
  361. /// <summary>
  362. /// Exclude specific properties of this type from being serialized
  363. /// </summary>
  364. public static string[] ExcludePropertyNames;
  365. public static void WriteFn<TSerializer>(TextWriter writer, object obj)
  366. {
  367. var serializer = JsWriter.GetTypeSerializer<TSerializer>();
  368. serializer.WriteString(writer, SerializeFn((T)obj));
  369. }
  370. public static object ParseFn(string str)
  371. {
  372. return DeSerializeFn(str);
  373. }
  374. }
  375. public enum JsonDateHandler
  376. {
  377. TimestampOffset,
  378. DCJSCompatible,
  379. ISO8601
  380. }
  381. public enum JsonTimeSpanHandler
  382. {
  383. /// <summary>
  384. /// Uses the xsd format like PT15H10M20S
  385. /// </summary>
  386. DurationFormat,
  387. /// <summary>
  388. /// Uses the standard .net ToString method of the TimeSpan class
  389. /// </summary>
  390. StandardFormat
  391. }
  392. }