SoapReflectionImporter.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. //
  2. // System.Xml.Serialization.SoapReflectionImporter
  3. //
  4. // Author:
  5. // Tim Coleman ([email protected])
  6. //
  7. // Copyright (C) Tim Coleman, 2002
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System.Reflection;
  30. using System.Xml;
  31. using System.Xml.Schema;
  32. using System.Collections;
  33. namespace System.Xml.Serialization {
  34. public class SoapReflectionImporter {
  35. SoapAttributeOverrides attributeOverrides;
  36. string initialDefaultNamespace;
  37. ArrayList includedTypes;
  38. ArrayList relatedMaps = new ArrayList ();
  39. ReflectionHelper helper = new ReflectionHelper();
  40. #region Constructors
  41. public SoapReflectionImporter (): this (null, null)
  42. {
  43. }
  44. public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides): this (attributeOverrides, null)
  45. {
  46. }
  47. public SoapReflectionImporter (string defaultNamespace): this (null, defaultNamespace)
  48. {
  49. }
  50. public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides, string defaultNamespace)
  51. {
  52. if (defaultNamespace == null) initialDefaultNamespace = String.Empty;
  53. else initialDefaultNamespace = defaultNamespace;
  54. if (attributeOverrides == null) this.attributeOverrides = new SoapAttributeOverrides();
  55. else this.attributeOverrides = attributeOverrides;
  56. }
  57. #endregion // Constructors
  58. #region Methods
  59. public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members)
  60. {
  61. return ImportMembersMapping (elementName, ns, members, true, true, false);
  62. }
  63. public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors)
  64. {
  65. return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, false);
  66. }
  67. public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate)
  68. {
  69. XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
  70. for (int n=0; n<members.Length; n++)
  71. {
  72. XmlTypeMapMember mapMem = CreateMapMember (members[n], ns);
  73. mapping[n] = new XmlMemberMapping (members[n].MemberName, ns, mapMem, true);
  74. }
  75. XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, writeAccessors, mapping);
  76. mps.RelatedMaps = relatedMaps;
  77. mps.Format = SerializationFormat.Encoded;
  78. Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
  79. mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, writeAccessors, false, null, extraTypes);
  80. return mps;
  81. }
  82. public XmlTypeMapping ImportTypeMapping (Type type)
  83. {
  84. return ImportTypeMapping (type, null);
  85. }
  86. public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
  87. {
  88. if (type == null)
  89. throw new ArgumentNullException ("type");
  90. if (type == typeof (void))
  91. throw new InvalidOperationException ("Type " + type.Name + " may not be serialized.");
  92. if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
  93. if (defaultNamespace == null) defaultNamespace = string.Empty;
  94. XmlTypeMapping map;
  95. switch (TypeTranslator.GetTypeData(type).SchemaType)
  96. {
  97. case SchemaTypes.Class: map = ImportClassMapping (type, defaultNamespace); break;
  98. case SchemaTypes.Array: map = ImportListMapping (type, defaultNamespace); break;
  99. case SchemaTypes.XmlNode: throw CreateTypeException (type);
  100. case SchemaTypes.Primitive: map = ImportPrimitiveMapping (type, defaultNamespace); break;
  101. case SchemaTypes.Enum: map = ImportEnumMapping (type, defaultNamespace); break;
  102. case SchemaTypes.XmlSerializable:
  103. default: throw new NotSupportedException ("Type " + type.FullName + " not supported for XML serialization");
  104. }
  105. map.RelatedMaps = relatedMaps;
  106. map.Format = SerializationFormat.Encoded;
  107. Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
  108. map.Source = new SoapTypeSerializationSource (type, attributeOverrides, defaultNamespace, extraTypes);
  109. return map;
  110. }
  111. XmlTypeMapping CreateTypeMapping (TypeData typeData, string defaultXmlType, string defaultNamespace)
  112. {
  113. string membersNamespace = defaultNamespace;
  114. bool includeInSchema = true;
  115. SoapAttributes atts = null;
  116. if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
  117. if (!typeData.IsListType)
  118. {
  119. if (attributeOverrides != null)
  120. atts = attributeOverrides[typeData.Type];
  121. if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
  122. throw new InvalidOperationException ("SoapType attribute may not be specified for the type " + typeData.FullTypeName);
  123. }
  124. if (atts == null)
  125. atts = new SoapAttributes (typeData.Type);
  126. if (atts.SoapType != null)
  127. {
  128. if (atts.SoapType.Namespace != null && atts.SoapType.Namespace != string.Empty)
  129. membersNamespace = atts.SoapType.Namespace;
  130. if (atts.SoapType.TypeName != null && atts.SoapType.TypeName != string.Empty)
  131. defaultXmlType = atts.SoapType.TypeName;
  132. includeInSchema = atts.SoapType.IncludeInSchema;
  133. }
  134. if (membersNamespace == null) membersNamespace = "";
  135. XmlTypeMapping map = new XmlTypeMapping (defaultXmlType, membersNamespace, typeData, defaultXmlType, membersNamespace);
  136. map.IncludeInSchema = includeInSchema;
  137. relatedMaps.Add (map);
  138. return map;
  139. }
  140. XmlTypeMapping ImportClassMapping (Type type, string defaultNamespace)
  141. {
  142. if (type.IsValueType) throw CreateStructException (type);
  143. if (type == typeof (object)) defaultNamespace = XmlSchema.Namespace;
  144. ReflectionHelper.CheckSerializableType (type, false);
  145. TypeData typeData = TypeTranslator.GetTypeData (type);
  146. XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
  147. if (map != null) return map;
  148. map = CreateTypeMapping (typeData, null, defaultNamespace);
  149. helper.RegisterClrType (map, type, map.Namespace);
  150. map.MultiReferenceType = true;
  151. ClassMap classMap = new ClassMap ();
  152. map.ObjectMap = classMap;
  153. // Import members
  154. try
  155. {
  156. ICollection members = GetReflectionMembers (type);
  157. foreach (XmlReflectionMember rmember in members)
  158. {
  159. if (rmember.SoapAttributes.SoapIgnore) continue;
  160. classMap.AddMember (CreateMapMember (rmember, map.Namespace));
  161. }
  162. }
  163. catch (Exception ex)
  164. {
  165. throw helper.CreateError (map, ex.Message);
  166. }
  167. // Import included classes
  168. SoapIncludeAttribute[] includes = (SoapIncludeAttribute[])type.GetCustomAttributes (typeof (SoapIncludeAttribute), false);
  169. for (int n=0; n<includes.Length; n++)
  170. {
  171. Type includedType = includes[n].Type;
  172. ImportTypeMapping (includedType);
  173. }
  174. if (type == typeof (object) && includedTypes != null)
  175. {
  176. foreach (Type intype in includedTypes)
  177. map.DerivedTypes.Add (ImportTypeMapping (intype));
  178. }
  179. // Register inheritance relations
  180. if (type.BaseType != null)
  181. {
  182. XmlTypeMapping bmap = ImportClassMapping (type.BaseType, defaultNamespace);
  183. if (type.BaseType != typeof (object))
  184. map.BaseMap = bmap;
  185. // At this point, derived classes of this map must be already registered
  186. RegisterDerivedMap (bmap, map);
  187. }
  188. return map;
  189. }
  190. void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
  191. {
  192. map.DerivedTypes.Add (derivedMap);
  193. map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
  194. if (map.BaseMap != null)
  195. RegisterDerivedMap (map.BaseMap, derivedMap);
  196. else {
  197. XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
  198. if (obmap != map)
  199. obmap.DerivedTypes.Add (derivedMap);
  200. }
  201. }
  202. string GetTypeNamespace (TypeData typeData, string defaultNamespace)
  203. {
  204. string membersNamespace = defaultNamespace;
  205. SoapAttributes atts = null;
  206. if (!typeData.IsListType)
  207. {
  208. if (attributeOverrides != null)
  209. atts = attributeOverrides[typeData.Type];
  210. }
  211. if (atts == null)
  212. atts = new SoapAttributes (typeData.Type);
  213. if (atts.SoapType != null)
  214. {
  215. if (atts.SoapType.Namespace != null && atts.SoapType.Namespace != string.Empty)
  216. membersNamespace = atts.SoapType.Namespace;
  217. }
  218. if (membersNamespace == null) return "";
  219. else return membersNamespace;
  220. }
  221. XmlTypeMapping ImportListMapping (Type type, string defaultNamespace)
  222. {
  223. TypeData typeData = TypeTranslator.GetTypeData (type);
  224. XmlTypeMapping map = helper.GetRegisteredClrType (type, XmlSerializer.EncodingNamespace);
  225. if (map != null) return map;
  226. ListMap obmap = new ListMap ();
  227. TypeData itemTypeData = typeData.ListItemTypeData;
  228. map = CreateTypeMapping (typeData, "Array", XmlSerializer.EncodingNamespace);
  229. helper.RegisterClrType (map, type, XmlSerializer.EncodingNamespace);
  230. map.MultiReferenceType = true;
  231. map.ObjectMap = obmap;
  232. XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, itemTypeData);
  233. if (elem.TypeData.IsComplexType) {
  234. elem.MappedType = ImportTypeMapping (typeData.ListItemType, defaultNamespace);
  235. elem.TypeData = elem.MappedType.TypeData;
  236. }
  237. elem.ElementName = "Item";
  238. elem.Namespace = string.Empty;
  239. elem.IsNullable = true; // By default, items are nullable
  240. XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
  241. list.Add (elem);
  242. obmap.ItemInfo = list;
  243. XmlTypeMapping objMap = ImportTypeMapping (typeof(object), defaultNamespace);
  244. objMap.DerivedTypes.Add (map);
  245. // Register any of the including types as a derived class of object
  246. SoapIncludeAttribute[] includes = (SoapIncludeAttribute[])type.GetCustomAttributes (typeof (SoapIncludeAttribute), false);
  247. for (int i = 0; i < includes.Length; i++)
  248. {
  249. Type includedType = includes[i].Type;
  250. objMap.DerivedTypes.Add(ImportTypeMapping (includedType, defaultNamespace));
  251. }
  252. return map;
  253. }
  254. XmlTypeMapping ImportPrimitiveMapping (Type type, string defaultNamespace)
  255. {
  256. TypeData typeData = TypeTranslator.GetTypeData (type);
  257. XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
  258. if (map != null) return map;
  259. map = CreateTypeMapping (typeData, null, defaultNamespace);
  260. helper.RegisterClrType (map, type, map.Namespace);
  261. return map;
  262. }
  263. XmlTypeMapping ImportEnumMapping (Type type, string defaultNamespace)
  264. {
  265. TypeData typeData = TypeTranslator.GetTypeData (type);
  266. XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
  267. if (map != null) return map;
  268. ReflectionHelper.CheckSerializableType (type, false);
  269. map = CreateTypeMapping (typeData, null, defaultNamespace);
  270. helper.RegisterClrType (map, type, map.Namespace);
  271. map.MultiReferenceType = true;
  272. string [] names = Enum.GetNames (type);
  273. EnumMap.EnumMapMember[] members = new EnumMap.EnumMapMember[names.Length];
  274. for (int n=0; n<names.Length; n++)
  275. {
  276. MemberInfo[] mem = type.GetMember (names[n]);
  277. string xmlName = names[n];
  278. object[] atts = mem[0].GetCustomAttributes (typeof(SoapEnumAttribute), false);
  279. if (atts.Length > 0) xmlName = ((SoapEnumAttribute)atts[0]).Name;
  280. members[n] = new EnumMap.EnumMapMember (xmlName, names[n]);
  281. }
  282. bool isFlags = type.GetCustomAttributes (typeof(FlagsAttribute),false).Length > 0;
  283. map.ObjectMap = new EnumMap (members, isFlags);
  284. ImportTypeMapping (typeof(object), defaultNamespace).DerivedTypes.Add (map);
  285. return map;
  286. }
  287. ICollection GetReflectionMembers (Type type)
  288. {
  289. ArrayList members = new ArrayList();
  290. PropertyInfo[] properties = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
  291. foreach (PropertyInfo prop in properties)
  292. {
  293. if (!prop.CanRead) continue;
  294. if (!prop.CanWrite && TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array)
  295. continue;
  296. SoapAttributes atts = attributeOverrides[type, prop.Name];
  297. if (atts == null) atts = new SoapAttributes (prop);
  298. if (atts.SoapIgnore) continue;
  299. XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
  300. members.Add (member);
  301. }
  302. FieldInfo[] fields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
  303. foreach (FieldInfo field in fields)
  304. {
  305. SoapAttributes atts = attributeOverrides[type, field.Name];
  306. if (atts == null) atts = new SoapAttributes (field);
  307. if (atts.SoapIgnore) continue;
  308. XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
  309. members.Add (member);
  310. }
  311. return members;
  312. }
  313. private XmlTypeMapMember CreateMapMember (XmlReflectionMember rmember, string defaultNamespace)
  314. {
  315. XmlTypeMapMember mapMember;
  316. SoapAttributes atts = rmember.SoapAttributes;
  317. TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
  318. if (atts.SoapAttribute != null)
  319. {
  320. // An attribute
  321. if (atts.SoapElement != null)
  322. throw new Exception ("SoapAttributeAttribute and SoapElementAttribute cannot be applied to the same member");
  323. XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
  324. if (atts.SoapAttribute.AttributeName == null)
  325. mapAttribute.AttributeName = rmember.MemberName;
  326. else
  327. mapAttribute.AttributeName = atts.SoapAttribute.AttributeName;
  328. mapAttribute.Namespace = (atts.SoapAttribute.Namespace != null) ? atts.SoapAttribute.Namespace : "";
  329. if (typeData.IsComplexType)
  330. mapAttribute.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace);
  331. typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapAttribute.DataType);
  332. mapMember = mapAttribute;
  333. }
  334. else
  335. {
  336. if (typeData.SchemaType == SchemaTypes.Array) mapMember = new XmlTypeMapMemberList ();
  337. else mapMember = new XmlTypeMapMemberElement ();
  338. if (atts.SoapElement != null && atts.SoapElement.DataType != null)
  339. typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapElement.DataType);
  340. // Creates an ElementInfo that identifies the element
  341. XmlTypeMapElementInfoList infoList = new XmlTypeMapElementInfoList();
  342. XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (mapMember, typeData);
  343. elem.ElementName = (atts.SoapElement != null && atts.SoapElement.ElementName != null) ? atts.SoapElement.ElementName : rmember.MemberName;
  344. elem.Namespace = string.Empty;
  345. elem.IsNullable = (atts.SoapElement != null) ? atts.SoapElement.IsNullable : false;
  346. if (typeData.IsComplexType)
  347. elem.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace);
  348. infoList.Add (elem);
  349. ((XmlTypeMapMemberElement)mapMember).ElementInfo = infoList;
  350. }
  351. mapMember.TypeData = typeData;
  352. mapMember.Name = rmember.MemberName;
  353. mapMember.IsReturnValue = rmember.IsReturnValue;
  354. return mapMember;
  355. }
  356. public void IncludeType (Type type)
  357. {
  358. if (type == null)
  359. throw new ArgumentNullException ("type");
  360. if (includedTypes == null) includedTypes = new ArrayList ();
  361. if (!includedTypes.Contains (type))
  362. includedTypes.Add (type);
  363. }
  364. public void IncludeTypes (ICustomAttributeProvider provider)
  365. {
  366. object[] ats = provider.GetCustomAttributes (typeof(SoapIncludeAttribute), true);
  367. foreach (SoapIncludeAttribute at in ats)
  368. IncludeType (at.Type);
  369. }
  370. Exception CreateTypeException (Type type)
  371. {
  372. return new NotSupportedException ("The type " + type.FullName + " may not be serialized with SOAP-encoded messages. Set the Use for your message to Literal");
  373. }
  374. Exception CreateStructException (Type type)
  375. {
  376. return new NotSupportedException ("Cannot serialize " + type.FullName + ". Nested structs are not supported with encoded SOAP");
  377. }
  378. #endregion // Methods
  379. }
  380. }