PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ServiceStack.Text/Common/WriteType.cs

https://github.com/pauldbau/ServiceStack.Text
C# | 309 lines | 252 code | 43 blank | 14 comment | 67 complexity | 29f0d483bd0ff609c034109311613619 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. //
  2. // https://github.com/ServiceStack/ServiceStack.Text
  3. // ServiceStack.Text: .NET C# POCO JSON, JSV and CSV Text Serializers.
  4. //
  5. // Authors:
  6. // Demis Bellot (demis.bellot@gmail.com)
  7. //
  8. // Copyright 2012 ServiceStack Ltd.
  9. //
  10. // Licensed under the same terms of ServiceStack: new BSD license.
  11. //
  12. using System;
  13. using System.IO;
  14. using ServiceStack.Text.Json;
  15. using ServiceStack.Text.Reflection;
  16. namespace ServiceStack.Text.Common
  17. {
  18. internal static class WriteType<T, TSerializer>
  19. where TSerializer : ITypeSerializer
  20. {
  21. private static readonly ITypeSerializer Serializer = JsWriter.GetTypeSerializer<TSerializer>();
  22. private static readonly WriteObjectDelegate CacheFn;
  23. internal static TypePropertyWriter[] PropertyWriters;
  24. private static readonly WriteObjectDelegate WriteTypeInfo;
  25. private static bool IsIncluded
  26. {
  27. get { return (JsConfig.IncludeTypeInfo || JsConfig<T>.IncludeTypeInfo); }
  28. }
  29. private static bool IsExcluded
  30. {
  31. get { return (JsConfig.ExcludeTypeInfo || JsConfig<T>.ExcludeTypeInfo); }
  32. }
  33. static WriteType()
  34. {
  35. if (typeof(T) == typeof(Object))
  36. {
  37. CacheFn = WriteObjectType;
  38. }
  39. else
  40. {
  41. CacheFn = Init() ? GetWriteFn() : WriteEmptyType;
  42. }
  43. if (IsIncluded)
  44. {
  45. WriteTypeInfo = TypeInfoWriter;
  46. }
  47. if (typeof(T).IsAbstract)
  48. {
  49. WriteTypeInfo = TypeInfoWriter;
  50. if (!JsConfig.PreferInterfaces || !typeof(T).IsInterface)
  51. {
  52. CacheFn = WriteAbstractProperties;
  53. }
  54. }
  55. }
  56. public static void TypeInfoWriter(TextWriter writer, object obj)
  57. {
  58. TryWriteTypeInfo(writer, obj);
  59. }
  60. private static bool ShouldSkipType() { return IsExcluded && !IsIncluded; }
  61. private static bool TryWriteSelfType(TextWriter writer)
  62. {
  63. if (ShouldSkipType()) return false;
  64. Serializer.WriteRawString(writer, JsConfig.TypeAttr);
  65. writer.Write(JsWriter.MapKeySeperator);
  66. Serializer.WriteRawString(writer, JsConfig.TypeWriter(typeof(T)));
  67. return true;
  68. }
  69. private static bool TryWriteTypeInfo(TextWriter writer, object obj)
  70. {
  71. if (obj == null || ShouldSkipType()) return false;
  72. Serializer.WriteRawString(writer, JsConfig.TypeAttr);
  73. writer.Write(JsWriter.MapKeySeperator);
  74. Serializer.WriteRawString(writer, JsConfig.TypeWriter(obj.GetType()));
  75. return true;
  76. }
  77. public static WriteObjectDelegate Write
  78. {
  79. get { return CacheFn; }
  80. }
  81. private static WriteObjectDelegate GetWriteFn()
  82. {
  83. return WriteProperties;
  84. }
  85. private static bool Init()
  86. {
  87. if (!typeof(T).IsClass && !typeof(T).IsInterface && !JsConfig.TreatAsRefType(typeof(T))) return false;
  88. var propertyInfos = TypeConfig<T>.Properties;
  89. var propertyNamesLength = propertyInfos.Length;
  90. PropertyWriters = new TypePropertyWriter[propertyNamesLength];
  91. if (propertyNamesLength == 0 && !JsState.IsWritingDynamic)
  92. {
  93. return typeof(T).IsDto();
  94. }
  95. // NOTE: very limited support for DataContractSerialization (DCS)
  96. // NOT supporting Serializable
  97. // support for DCS is intended for (re)Name of properties and Ignore by NOT having a DataMember present
  98. var isDataContract = typeof(T).IsDto();
  99. for (var i = 0; i < propertyNamesLength; i++)
  100. {
  101. var propertyInfo = propertyInfos[i];
  102. string propertyName, propertyNameCLSFriendly, propertyNameLowercaseUnderscore;
  103. if (isDataContract)
  104. {
  105. var dcsDataMember = propertyInfo.GetDataMember();
  106. if (dcsDataMember == null) continue;
  107. propertyName = dcsDataMember.Name ?? propertyInfo.Name;
  108. propertyNameCLSFriendly = dcsDataMember.Name ?? propertyName.ToCamelCase();
  109. propertyNameLowercaseUnderscore = dcsDataMember.Name ?? propertyName.ToLowercaseUnderscore();
  110. }
  111. else
  112. {
  113. propertyName = propertyInfo.Name;
  114. propertyNameCLSFriendly = propertyName.ToCamelCase();
  115. propertyNameLowercaseUnderscore = propertyName.ToLowercaseUnderscore();
  116. }
  117. var propertyType = propertyInfo.PropertyType;
  118. var suppressDefaultValue = propertyType.IsValueType && JsConfig.HasSerializeFn.Contains(propertyType)
  119. ? propertyType.GetDefaultValue()
  120. : null;
  121. PropertyWriters[i] = new TypePropertyWriter
  122. (
  123. propertyName,
  124. propertyNameCLSFriendly,
  125. propertyNameLowercaseUnderscore,
  126. propertyInfo.GetValueGetter<T>(),
  127. Serializer.GetWriteFn(propertyType),
  128. suppressDefaultValue
  129. );
  130. }
  131. return true;
  132. }
  133. internal struct TypePropertyWriter
  134. {
  135. internal string PropertyName
  136. {
  137. get
  138. {
  139. return (JsConfig.EmitCamelCaseNames)
  140. ? propertyNameCLSFriendly
  141. : (JsConfig.EmitLowercaseUnderscoreNames)
  142. ? propertyNameLowercaseUnderscore
  143. : propertyName;
  144. }
  145. }
  146. internal readonly string propertyName;
  147. internal readonly string propertyNameCLSFriendly;
  148. internal readonly string propertyNameLowercaseUnderscore;
  149. internal readonly Func<T, object> GetterFn;
  150. internal readonly WriteObjectDelegate WriteFn;
  151. internal readonly object DefaultValue;
  152. public TypePropertyWriter(string propertyName, string propertyNameCLSFriendly, string propertyNameLowercaseUnderscore,
  153. Func<T, object> getterFn, WriteObjectDelegate writeFn, object defaultValue)
  154. {
  155. this.propertyName = propertyName;
  156. this.propertyNameCLSFriendly = propertyNameCLSFriendly;
  157. this.propertyNameLowercaseUnderscore = propertyNameLowercaseUnderscore;
  158. this.GetterFn = getterFn;
  159. this.WriteFn = writeFn;
  160. this.DefaultValue = defaultValue;
  161. }
  162. }
  163. public static void WriteObjectType(TextWriter writer, object value)
  164. {
  165. writer.Write(JsWriter.EmptyMap);
  166. }
  167. public static void WriteEmptyType(TextWriter writer, object value)
  168. {
  169. if (WriteTypeInfo != null || JsState.IsWritingDynamic)
  170. {
  171. writer.Write(JsWriter.MapStartChar);
  172. if (!(JsConfig.PreferInterfaces && TryWriteSelfType(writer)))
  173. {
  174. TryWriteTypeInfo(writer, value);
  175. }
  176. writer.Write(JsWriter.MapEndChar);
  177. return;
  178. }
  179. writer.Write(JsWriter.EmptyMap);
  180. }
  181. public static void WriteAbstractProperties(TextWriter writer, object value)
  182. {
  183. if (value == null)
  184. {
  185. writer.Write(JsWriter.EmptyMap);
  186. return;
  187. }
  188. var valueType = value.GetType();
  189. if (valueType.IsAbstract)
  190. {
  191. WriteProperties(writer, value);
  192. return;
  193. }
  194. var writeFn = Serializer.GetWriteFn(valueType);
  195. if (!JsConfig<T>.ExcludeTypeInfo) JsState.IsWritingDynamic = true;
  196. writeFn(writer, value);
  197. if (!JsConfig<T>.ExcludeTypeInfo) JsState.IsWritingDynamic = false;
  198. }
  199. public static void WriteProperties(TextWriter writer, object value)
  200. {
  201. if (typeof(TSerializer) == typeof(JsonTypeSerializer) && JsState.WritingKeyCount > 0)
  202. writer.Write(JsWriter.QuoteChar);
  203. writer.Write(JsWriter.MapStartChar);
  204. var i = 0;
  205. if (WriteTypeInfo != null || JsState.IsWritingDynamic)
  206. {
  207. if (JsConfig.PreferInterfaces && TryWriteSelfType(writer)) i++;
  208. else if (TryWriteTypeInfo(writer, value)) i++;
  209. JsState.IsWritingDynamic = false;
  210. }
  211. if (PropertyWriters != null)
  212. {
  213. var len = PropertyWriters.Length;
  214. for (int index = 0; index < len; index++)
  215. {
  216. var propertyWriter = PropertyWriters[index];
  217. var propertyValue = value != null
  218. ? propertyWriter.GetterFn((T)value)
  219. : null;
  220. if ((propertyValue == null
  221. || (propertyWriter.DefaultValue != null && propertyWriter.DefaultValue.Equals(propertyValue)))
  222. && !Serializer.IncludeNullValues) continue;
  223. if (i++ > 0)
  224. writer.Write(JsWriter.ItemSeperator);
  225. Serializer.WritePropertyName(writer, propertyWriter.PropertyName);
  226. writer.Write(JsWriter.MapKeySeperator);
  227. if (typeof(TSerializer) == typeof(JsonTypeSerializer)) JsState.IsWritingValue = true;
  228. if (propertyValue == null)
  229. {
  230. writer.Write(JsonUtils.Null);
  231. }
  232. else
  233. {
  234. propertyWriter.WriteFn(writer, propertyValue);
  235. }
  236. if (typeof(TSerializer) == typeof(JsonTypeSerializer)) JsState.IsWritingValue = false;
  237. }
  238. }
  239. writer.Write(JsWriter.MapEndChar);
  240. if (typeof(TSerializer) == typeof(JsonTypeSerializer) && JsState.WritingKeyCount > 0)
  241. writer.Write(JsWriter.QuoteChar);
  242. }
  243. public static void WriteQueryString(TextWriter writer, object value)
  244. {
  245. try
  246. {
  247. JsState.QueryStringMode = true;
  248. var i = 0;
  249. foreach (var propertyWriter in PropertyWriters)
  250. {
  251. var propertyValue = propertyWriter.GetterFn((T)value);
  252. if (propertyValue == null) continue;
  253. if (i++ > 0)
  254. writer.Write('&');
  255. Serializer.WritePropertyName(writer, propertyWriter.PropertyName);
  256. writer.Write('=');
  257. propertyWriter.WriteFn(writer, propertyValue);
  258. }
  259. }
  260. finally
  261. {
  262. JsState.QueryStringMode = false;
  263. }
  264. }
  265. }
  266. }