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

http://yafnet.codeplex.com · C# · 284 lines · 228 code · 42 blank · 14 comment · 61 complexity · a95310bf08c568815db0019602133209 MD5 · raw file

  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. using System.Linq;
  17. using System.Runtime.Serialization;
  18. namespace ServiceStack.Text.Common
  19. {
  20. internal static class WriteType<T, TSerializer>
  21. where TSerializer : ITypeSerializer
  22. {
  23. private static readonly ITypeSerializer Serializer = JsWriter.GetTypeSerializer<TSerializer>();
  24. private static readonly WriteObjectDelegate CacheFn;
  25. internal static TypePropertyWriter[] PropertyWriters;
  26. private static readonly WriteObjectDelegate WriteTypeInfo;
  27. private static bool IsIncluded
  28. {
  29. get { return (JsConfig.IncludeTypeInfo || JsConfig<T>.IncludeTypeInfo); }
  30. }
  31. private static bool IsExcluded
  32. {
  33. get { return (JsConfig.ExcludeTypeInfo || JsConfig<T>.ExcludeTypeInfo); }
  34. }
  35. static WriteType()
  36. {
  37. CacheFn = Init() ? GetWriteFn() : WriteEmptyType;
  38. if (IsIncluded)
  39. {
  40. WriteTypeInfo = TypeInfoWriter;
  41. }
  42. if (typeof(T).IsAbstract)
  43. {
  44. WriteTypeInfo = TypeInfoWriter;
  45. if (!typeof(T).IsInterface)
  46. {
  47. CacheFn = WriteAbstractProperties;
  48. }
  49. }
  50. }
  51. public static void TypeInfoWriter(TextWriter writer, object obj)
  52. {
  53. TryWriteTypeInfo(writer, obj);
  54. }
  55. private static bool ShouldSkipType() { return IsExcluded && !IsIncluded; }
  56. private static bool TryWriteSelfType (TextWriter writer) {
  57. if (ShouldSkipType()) return false;
  58. Serializer.WriteRawString(writer, JsConfig.TypeAttr);
  59. writer.Write(JsWriter.MapKeySeperator);
  60. Serializer.WriteRawString(writer, JsConfig.TypeWriter(typeof(T)));
  61. return true;
  62. }
  63. private static bool TryWriteTypeInfo(TextWriter writer, object obj)
  64. {
  65. if (obj == null || ShouldSkipType()) return false;
  66. Serializer.WriteRawString(writer, JsConfig.TypeAttr);
  67. writer.Write(JsWriter.MapKeySeperator);
  68. Serializer.WriteRawString(writer, JsConfig.TypeWriter(obj.GetType()));
  69. return true;
  70. }
  71. public static WriteObjectDelegate Write
  72. {
  73. get { return CacheFn; }
  74. }
  75. private static WriteObjectDelegate GetWriteFn()
  76. {
  77. return WriteProperties;
  78. }
  79. private static bool Init()
  80. {
  81. if (!typeof(T).IsClass && !typeof(T).IsInterface && !JsConfig.TreatAsRefType(typeof(T))) return false;
  82. var propertyInfos = TypeConfig<T>.Properties;
  83. var propertyNamesLength = propertyInfos.Length;
  84. PropertyWriters = new TypePropertyWriter[propertyNamesLength];
  85. if (propertyNamesLength == 0 && !JsState.IsWritingDynamic)
  86. {
  87. return typeof(T).IsDto();
  88. }
  89. // NOTE: very limited support for DataContractSerialization (DCS)
  90. // NOT supporting Serializable
  91. // support for DCS is intended for (re)Name of properties and Ignore by NOT having a DataMember present
  92. var isDataContract = typeof(T).GetCustomAttributes(typeof(DataContractAttribute), false).Any();
  93. for (var i = 0; i < propertyNamesLength; i++)
  94. {
  95. var propertyInfo = propertyInfos[i];
  96. string propertyName, propertyNameCLSFriendly, propertyNameLowercaseUnderscore;
  97. if (isDataContract)
  98. {
  99. var dcsDataMember = propertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), false).FirstOrDefault() as DataMemberAttribute;
  100. if (dcsDataMember == null) continue;
  101. propertyName = dcsDataMember.Name ?? propertyInfo.Name;
  102. propertyNameCLSFriendly = dcsDataMember.Name ?? propertyName.ToCamelCase();
  103. propertyNameLowercaseUnderscore = dcsDataMember.Name ?? propertyName.ToLowercaseUnderscore();
  104. }
  105. else
  106. {
  107. propertyName = propertyInfo.Name;
  108. propertyNameCLSFriendly = propertyName.ToCamelCase();
  109. propertyNameLowercaseUnderscore = propertyName.ToLowercaseUnderscore();
  110. }
  111. var propertyType = propertyInfo.PropertyType;
  112. var suppressDefaultValue = propertyType.IsValueType && JsConfig.HasSerializeFn.Contains(propertyType)
  113. ? propertyType.GetDefaultValue()
  114. : null;
  115. PropertyWriters[i] = new TypePropertyWriter
  116. (
  117. propertyName,
  118. propertyNameCLSFriendly,
  119. propertyNameLowercaseUnderscore,
  120. propertyInfo.GetValueGetter<T>(),
  121. Serializer.GetWriteFn(propertyType),
  122. suppressDefaultValue
  123. );
  124. }
  125. return true;
  126. }
  127. internal struct TypePropertyWriter
  128. {
  129. internal string PropertyName
  130. {
  131. get
  132. {
  133. return (JsConfig.EmitCamelCaseNames)
  134. ? propertyNameCLSFriendly
  135. : (JsConfig.EmitLowercaseUnderscoreNames)
  136. ? propertyNameLowercaseUnderscore
  137. : propertyName;
  138. }
  139. }
  140. internal readonly string propertyName;
  141. internal readonly string propertyNameCLSFriendly;
  142. internal readonly string propertyNameLowercaseUnderscore;
  143. internal readonly Func<T, object> GetterFn;
  144. internal readonly WriteObjectDelegate WriteFn;
  145. internal readonly object DefaultValue;
  146. public TypePropertyWriter(string propertyName, string propertyNameCLSFriendly, string propertyNameLowercaseUnderscore,
  147. Func<T, object> getterFn, WriteObjectDelegate writeFn, object defaultValue)
  148. {
  149. this.propertyName = propertyName;
  150. this.propertyNameCLSFriendly = propertyNameCLSFriendly;
  151. this.propertyNameLowercaseUnderscore = propertyNameLowercaseUnderscore;
  152. this.GetterFn = getterFn;
  153. this.WriteFn = writeFn;
  154. this.DefaultValue = defaultValue;
  155. }
  156. }
  157. public static void WriteEmptyType(TextWriter writer, object value)
  158. {
  159. writer.Write(JsWriter.EmptyMap);
  160. }
  161. public static void WriteAbstractProperties(TextWriter writer, object value)
  162. {
  163. if (value == null)
  164. {
  165. writer.Write(JsWriter.EmptyMap);
  166. return;
  167. }
  168. var valueType = value.GetType();
  169. if (valueType.IsAbstract)
  170. {
  171. WriteProperties(writer, value);
  172. return;
  173. }
  174. var writeFn = Serializer.GetWriteFn(valueType);
  175. if (!JsConfig<T>.ExcludeTypeInfo) JsState.IsWritingDynamic = true;
  176. writeFn(writer, value);
  177. if (!JsConfig<T>.ExcludeTypeInfo) JsState.IsWritingDynamic = false;
  178. }
  179. public static void WriteProperties(TextWriter writer, object value)
  180. {
  181. if (typeof(TSerializer) == typeof(JsonTypeSerializer) && JsState.WritingKeyCount > 0)
  182. writer.Write(JsWriter.QuoteChar);
  183. writer.Write(JsWriter.MapStartChar);
  184. var i = 0;
  185. if (WriteTypeInfo != null || JsState.IsWritingDynamic)
  186. {
  187. if (JsConfig.PreferInterfaces && TryWriteSelfType(writer)) i++;
  188. else if (TryWriteTypeInfo(writer, value)) i++;
  189. }
  190. if (PropertyWriters != null)
  191. {
  192. var len = PropertyWriters.Length;
  193. for (int index = 0; index < len; index++)
  194. {
  195. var propertyWriter = PropertyWriters[index];
  196. var propertyValue = value != null
  197. ? propertyWriter.GetterFn((T)value)
  198. : null;
  199. if ((propertyValue == null
  200. || (propertyWriter.DefaultValue != null && propertyWriter.DefaultValue.Equals(propertyValue)))
  201. && !Serializer.IncludeNullValues) continue;
  202. if (i++ > 0)
  203. writer.Write(JsWriter.ItemSeperator);
  204. Serializer.WritePropertyName(writer, propertyWriter.PropertyName);
  205. writer.Write(JsWriter.MapKeySeperator);
  206. if (typeof (TSerializer) == typeof (JsonTypeSerializer)) JsState.IsWritingValue = true;
  207. if (propertyValue == null)
  208. {
  209. writer.Write(JsonUtils.Null);
  210. }
  211. else
  212. {
  213. propertyWriter.WriteFn(writer, propertyValue);
  214. }
  215. if (typeof(TSerializer) == typeof(JsonTypeSerializer)) JsState.IsWritingValue = false;
  216. }
  217. }
  218. writer.Write(JsWriter.MapEndChar);
  219. if (typeof(TSerializer) == typeof(JsonTypeSerializer) && JsState.WritingKeyCount > 0)
  220. writer.Write(JsWriter.QuoteChar);
  221. }
  222. public static void WriteQueryString(TextWriter writer, object value)
  223. {
  224. var i = 0;
  225. foreach (var propertyWriter in PropertyWriters)
  226. {
  227. var propertyValue = propertyWriter.GetterFn((T)value);
  228. if (propertyValue == null) continue;
  229. var propertyValueString = propertyValue as string;
  230. if (propertyValueString != null)
  231. {
  232. propertyValue = propertyValueString.UrlEncode();
  233. }
  234. if (i++ > 0)
  235. writer.Write('&');
  236. Serializer.WritePropertyName(writer, propertyWriter.PropertyName);
  237. writer.Write('=');
  238. propertyWriter.WriteFn(writer, propertyValue);
  239. }
  240. }
  241. }
  242. }