2 // System.Xml.Serialization.SoapReflectionImporter
5 // Tim Coleman (tim@timcoleman.com)
7 // Copyright (C) Tim Coleman, 2002
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Reflection;
33 using System.Xml.Schema;
34 using System.Collections;
36 namespace System.Xml.Serialization {
37 public class SoapReflectionImporter {
39 SoapAttributeOverrides attributeOverrides;
40 string initialDefaultNamespace;
41 ArrayList includedTypes;
42 ArrayList relatedMaps = new ArrayList ();
43 ReflectionHelper helper = new ReflectionHelper();
47 public SoapReflectionImporter (): this (null, null)
51 public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides): this (attributeOverrides, null)
55 public SoapReflectionImporter (string defaultNamespace): this (null, defaultNamespace)
59 public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides, string defaultNamespace)
61 if (defaultNamespace == null) initialDefaultNamespace = String.Empty;
62 else initialDefaultNamespace = defaultNamespace;
64 if (attributeOverrides == null) this.attributeOverrides = new SoapAttributeOverrides();
65 else this.attributeOverrides = attributeOverrides;
68 #endregion // Constructors
72 public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members)
74 return ImportMembersMapping (elementName, ns, members, true, true, false);
77 public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors)
79 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, false);
82 public XmlMembersMapping ImportMembersMapping (string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate)
84 elementName = XmlConvert.EncodeLocalName (elementName);
85 XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
86 for (int n=0; n<members.Length; n++)
88 XmlTypeMapMember mapMem = CreateMapMember (members[n], ns);
89 mapping[n] = new XmlMemberMapping (XmlConvert.EncodeLocalName (members[n].MemberName), ns, mapMem, true);
91 XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, writeAccessors, mapping);
92 mps.RelatedMaps = relatedMaps;
93 mps.Format = SerializationFormat.Encoded;
94 Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
95 mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, writeAccessors, false, null, extraTypes);
99 public XmlTypeMapping ImportTypeMapping (Type type)
101 return ImportTypeMapping (type, null);
104 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
107 throw new ArgumentNullException ("type");
109 if (type == typeof (void))
110 throw new InvalidOperationException ("Type " + type.Name + " may not be serialized.");
112 string oldNs = initialDefaultNamespace;
113 if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
114 if (defaultNamespace == null) defaultNamespace = string.Empty;
115 initialDefaultNamespace = defaultNamespace;
118 switch (TypeTranslator.GetTypeData(type).SchemaType)
120 case SchemaTypes.Class: map = ImportClassMapping (type, defaultNamespace); break;
121 case SchemaTypes.Array: map = ImportListMapping (type, defaultNamespace); break;
122 case SchemaTypes.XmlNode: throw CreateTypeException (type);
123 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (type, defaultNamespace); break;
124 case SchemaTypes.Enum: map = ImportEnumMapping (type, defaultNamespace); break;
125 case SchemaTypes.XmlSerializable:
126 default: throw new NotSupportedException ("Type " + type.FullName + " not supported for XML serialization");
128 map.RelatedMaps = relatedMaps;
129 map.Format = SerializationFormat.Encoded;
130 Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
131 map.Source = new SoapTypeSerializationSource (type, attributeOverrides, defaultNamespace, extraTypes);
133 initialDefaultNamespace = oldNs;
137 XmlTypeMapping CreateTypeMapping (TypeData typeData, string defaultXmlType, string defaultNamespace)
139 string membersNamespace = defaultNamespace;
140 bool includeInSchema = true;
142 SoapAttributes atts = null;
143 if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
145 if (!typeData.IsListType)
147 if (attributeOverrides != null)
148 atts = attributeOverrides[typeData.Type];
150 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
151 throw new InvalidOperationException ("SoapType attribute may not be specified for the type " + typeData.FullTypeName);
155 atts = new SoapAttributes (typeData.Type);
157 if (atts.SoapType != null)
159 if (atts.SoapType.Namespace != null && atts.SoapType.Namespace != string.Empty)
160 membersNamespace = atts.SoapType.Namespace;
162 if (atts.SoapType.TypeName != null && atts.SoapType.TypeName != string.Empty)
163 defaultXmlType = XmlConvert.EncodeLocalName (atts.SoapType.TypeName);
165 includeInSchema = atts.SoapType.IncludeInSchema;
168 if (membersNamespace == null) membersNamespace = "";
169 XmlTypeMapping map = new XmlTypeMapping (defaultXmlType, membersNamespace, typeData, defaultXmlType, membersNamespace);
170 map.IncludeInSchema = includeInSchema;
171 relatedMaps.Add (map);
176 XmlTypeMapping ImportClassMapping (Type type, string defaultNamespace)
178 if (type.IsValueType) throw CreateStructException (type);
179 if (type == typeof (object)) defaultNamespace = XmlSchema.Namespace;
181 ReflectionHelper.CheckSerializableType (type, false);
183 TypeData typeData = TypeTranslator.GetTypeData (type);
184 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
185 if (map != null) return map;
187 map = CreateTypeMapping (typeData, null, defaultNamespace);
188 helper.RegisterClrType (map, type, map.Namespace);
189 map.MultiReferenceType = true;
191 ClassMap classMap = new ClassMap ();
192 map.ObjectMap = classMap;
198 ICollection members = GetReflectionMembers (type);
199 foreach (XmlReflectionMember rmember in members)
201 if (rmember.SoapAttributes.SoapIgnore) continue;
202 classMap.AddMember (CreateMapMember (rmember, map.Namespace));
207 throw helper.CreateError (map, ex.Message);
210 // Import included classes
212 SoapIncludeAttribute[] includes = (SoapIncludeAttribute[])type.GetCustomAttributes (typeof (SoapIncludeAttribute), false);
213 for (int n=0; n<includes.Length; n++)
215 Type includedType = includes[n].Type;
216 ImportTypeMapping (includedType);
219 if (type == typeof (object) && includedTypes != null)
221 foreach (Type intype in includedTypes)
222 map.DerivedTypes.Add (ImportTypeMapping (intype));
225 // Register inheritance relations
227 if (type.BaseType != null)
229 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, defaultNamespace);
231 if (type.BaseType != typeof (object))
234 // At this point, derived classes of this map must be already registered
236 RegisterDerivedMap (bmap, map);
242 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
244 map.DerivedTypes.Add (derivedMap);
245 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
247 if (map.BaseMap != null)
248 RegisterDerivedMap (map.BaseMap, derivedMap);
250 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
252 obmap.DerivedTypes.Add (derivedMap);
256 string GetTypeNamespace (TypeData typeData, string defaultNamespace)
258 string membersNamespace = defaultNamespace;
260 SoapAttributes atts = null;
262 if (!typeData.IsListType)
264 if (attributeOverrides != null)
265 atts = attributeOverrides[typeData.Type];
269 atts = new SoapAttributes (typeData.Type);
271 if (atts.SoapType != null)
273 if (atts.SoapType.Namespace != null && atts.SoapType.Namespace != string.Empty)
274 membersNamespace = atts.SoapType.Namespace;
277 if (membersNamespace == null) return "";
278 else return membersNamespace;
281 XmlTypeMapping ImportListMapping (Type type, string defaultNamespace)
283 TypeData typeData = TypeTranslator.GetTypeData (type);
284 XmlTypeMapping map = helper.GetRegisteredClrType (type, XmlSerializer.EncodingNamespace);
285 if (map != null) return map;
287 ListMap obmap = new ListMap ();
288 TypeData itemTypeData = typeData.ListItemTypeData;
290 map = CreateTypeMapping (typeData, "Array", XmlSerializer.EncodingNamespace);
291 helper.RegisterClrType (map, type, XmlSerializer.EncodingNamespace);
292 map.MultiReferenceType = true;
293 map.ObjectMap = obmap;
295 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, itemTypeData);
297 if (elem.TypeData.IsComplexType) {
298 elem.MappedType = ImportTypeMapping (typeData.ListItemType, defaultNamespace);
299 elem.TypeData = elem.MappedType.TypeData;
302 elem.ElementName = "Item";
303 elem.Namespace = string.Empty;
304 elem.IsNullable = true; // By default, items are nullable
306 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
309 obmap.ItemInfo = list;
310 XmlTypeMapping objMap = ImportTypeMapping (typeof(object), defaultNamespace);
311 objMap.DerivedTypes.Add (map);
313 // Register any of the including types as a derived class of object
314 SoapIncludeAttribute[] includes = (SoapIncludeAttribute[])type.GetCustomAttributes (typeof (SoapIncludeAttribute), false);
315 for (int i = 0; i < includes.Length; i++)
317 Type includedType = includes[i].Type;
318 objMap.DerivedTypes.Add(ImportTypeMapping (includedType, defaultNamespace));
324 XmlTypeMapping ImportPrimitiveMapping (Type type, string defaultNamespace)
326 TypeData typeData = TypeTranslator.GetTypeData (type);
327 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
328 if (map != null) return map;
329 map = CreateTypeMapping (typeData, null, defaultNamespace);
330 helper.RegisterClrType (map, type, map.Namespace);
335 XmlTypeMapping ImportEnumMapping (Type type, string defaultNamespace)
337 TypeData typeData = TypeTranslator.GetTypeData (type);
338 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, defaultNamespace));
339 if (map != null) return map;
341 ReflectionHelper.CheckSerializableType (type, false);
343 map = CreateTypeMapping (typeData, null, defaultNamespace);
344 helper.RegisterClrType (map, type, map.Namespace);
346 map.MultiReferenceType = true;
348 string [] names = Enum.GetNames (type);
349 EnumMap.EnumMapMember[] members = new EnumMap.EnumMapMember[names.Length];
350 for (int n=0; n<names.Length; n++)
352 MemberInfo[] mem = type.GetMember (names[n]);
353 string xmlName = names[n];
354 object[] atts = mem[0].GetCustomAttributes (typeof(SoapEnumAttribute), false);
355 if (atts.Length > 0) xmlName = ((SoapEnumAttribute)atts[0]).Name;
356 members[n] = new EnumMap.EnumMapMember (XmlConvert.EncodeLocalName (xmlName), names[n]);
359 bool isFlags = type.GetCustomAttributes (typeof(FlagsAttribute),false).Length > 0;
360 map.ObjectMap = new EnumMap (members, isFlags);
361 ImportTypeMapping (typeof(object), defaultNamespace).DerivedTypes.Add (map);
365 ICollection GetReflectionMembers (Type type)
367 ArrayList members = new ArrayList();
368 PropertyInfo[] properties = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
369 foreach (PropertyInfo prop in properties)
371 if (!prop.CanRead) continue;
372 if (!prop.CanWrite && (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray))
375 SoapAttributes atts = attributeOverrides[type, prop.Name];
376 if (atts == null) atts = new SoapAttributes (prop);
377 if (atts.SoapIgnore) continue;
378 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
379 members.Add (member);
382 FieldInfo[] fields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
383 foreach (FieldInfo field in fields)
385 SoapAttributes atts = attributeOverrides[type, field.Name];
386 if (atts == null) atts = new SoapAttributes (field);
387 if (atts.SoapIgnore) continue;
388 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
389 members.Add (member);
394 private XmlTypeMapMember CreateMapMember (XmlReflectionMember rmember, string defaultNamespace)
396 XmlTypeMapMember mapMember;
397 SoapAttributes atts = rmember.SoapAttributes;
398 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
400 if (atts.SoapAttribute != null)
404 if (atts.SoapElement != null)
405 throw new Exception ("SoapAttributeAttribute and SoapElementAttribute cannot be applied to the same member");
407 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
408 if (atts.SoapAttribute.AttributeName.Length == 0)
409 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (rmember.MemberName);
411 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (atts.SoapAttribute.AttributeName);
413 mapAttribute.Namespace = (atts.SoapAttribute.Namespace != null) ? atts.SoapAttribute.Namespace : "";
414 if (typeData.IsComplexType)
415 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace);
417 typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapAttribute.DataType);
418 mapMember = mapAttribute;
422 if (typeData.SchemaType == SchemaTypes.Array) mapMember = new XmlTypeMapMemberList ();
423 else mapMember = new XmlTypeMapMemberElement ();
425 if (atts.SoapElement != null && atts.SoapElement.DataType.Length != 0)
426 typeData = TypeTranslator.GetTypeData (rmember.MemberType, atts.SoapElement.DataType);
428 // Creates an ElementInfo that identifies the element
429 XmlTypeMapElementInfoList infoList = new XmlTypeMapElementInfoList();
430 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (mapMember, typeData);
432 elem.ElementName = XmlConvert.EncodeLocalName ((atts.SoapElement != null && atts.SoapElement.ElementName.Length != 0) ? atts.SoapElement.ElementName : rmember.MemberName);
433 elem.Namespace = string.Empty;
434 elem.IsNullable = (atts.SoapElement != null) ? atts.SoapElement.IsNullable : false;
435 if (typeData.IsComplexType)
436 elem.MappedType = ImportTypeMapping (typeData.Type, defaultNamespace);
439 ((XmlTypeMapMemberElement)mapMember).ElementInfo = infoList;
442 mapMember.TypeData = typeData;
443 mapMember.Name = rmember.MemberName;
444 mapMember.IsReturnValue = rmember.IsReturnValue;
448 public void IncludeType (Type type)
451 throw new ArgumentNullException ("type");
453 if (includedTypes == null) includedTypes = new ArrayList ();
454 if (!includedTypes.Contains (type))
455 includedTypes.Add (type);
458 public void IncludeTypes (ICustomAttributeProvider provider)
460 object[] ats = provider.GetCustomAttributes (typeof(SoapIncludeAttribute), true);
461 foreach (SoapIncludeAttribute at in ats)
462 IncludeType (at.Type);
465 Exception CreateTypeException (Type type)
467 return new NotSupportedException ("The type " + type.FullName + " may not be serialized with SOAP-encoded messages. Set the Use for your message to Literal");
470 Exception CreateStructException (Type type)
472 return new NotSupportedException ("Cannot serialize " + type.FullName + ". Nested structs are not supported with encoded SOAP");
478 #endregion // Methods