2 // System.Xml.Serialization.XmlReflectionImporter
5 // Tim Coleman (tim@timcoleman.com)
6 // Erik LeBel (eriklebel@yahoo.ca)
7 // Lluis Sanchez Gual (lluis@ximian.com)
9 // Copyright (C) Tim Coleman, 2002
10 // (C) 2003 Erik LeBel
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Globalization;
36 using System.Reflection;
37 using System.Xml.Schema;
39 namespace System.Xml.Serialization {
40 public class XmlReflectionImporter {
42 string initialDefaultNamespace;
43 XmlAttributeOverrides attributeOverrides;
44 ArrayList includedTypes;
45 ReflectionHelper helper = new ReflectionHelper();
46 int arrayChoiceCount = 1;
47 ArrayList relatedMaps = new ArrayList ();
48 bool allowPrivateTypes = false;
50 static readonly string errSimple = "Cannot serialize object of type '{0}'. Base " +
51 "type '{1}' has simpleContent and can be only extended by adding XmlAttribute " +
52 "elements. Please consider changing XmlText member of the base class to string array";
54 static readonly string errSimple2 = "Cannot serialize object of type '{0}'. " +
55 "Consider changing type of XmlText member '{1}' from '{2}' to string or string array";
59 public XmlReflectionImporter ()
64 public XmlReflectionImporter (string defaultNamespace)
65 : this (null, defaultNamespace)
69 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides)
70 : this (attributeOverrides, null)
74 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides, string defaultNamespace)
76 if (defaultNamespace == null)
77 this.initialDefaultNamespace = String.Empty;
79 this.initialDefaultNamespace = defaultNamespace;
81 if (attributeOverrides == null)
82 this.attributeOverrides = new XmlAttributeOverrides();
84 this.attributeOverrides = attributeOverrides;
89 helper = new ReflectionHelper();
94 internal bool AllowPrivateTypes
96 get { return allowPrivateTypes; }
97 set { allowPrivateTypes = value; }
100 #endregion // Constructors
104 public XmlMembersMapping ImportMembersMapping (string elementName,
106 XmlReflectionMember [] members,
107 bool hasWrapperElement)
109 // Reset (); Disabled. See ChangeLog
111 XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
112 for (int n=0; n<members.Length; n++)
114 XmlTypeMapMember mapMem = CreateMapMember (null, members[n], ns);
115 mapping[n] = new XmlMemberMapping (members[n].MemberName, ns, mapMem, false);
117 elementName = XmlConvert.EncodeLocalName (elementName);
118 XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, mapping);
119 mps.RelatedMaps = relatedMaps;
120 mps.Format = SerializationFormat.Literal;
121 Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
122 mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
123 if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
129 public XmlMembersMapping ImportMembersMapping (string elementName,
131 XmlReflectionMember[] members,
132 bool hasWrapperElement,
135 throw new NotImplementedException ();
139 public XmlMembersMapping ImportMembersMapping (string elementName,
141 XmlReflectionMember[] members,
142 bool hasWrapperElement,
146 throw new NotImplementedException ();
150 public XmlTypeMapping ImportTypeMapping (Type type)
152 return ImportTypeMapping (type, null, null);
155 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
157 return ImportTypeMapping (type, null, defaultNamespace);
160 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute group)
162 return ImportTypeMapping (type, group, null);
165 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
168 throw new ArgumentNullException ("type");
170 if (type == typeof (void))
171 throw new NotSupportedException ("The type " + type.FullName + " may not be serialized.");
173 return ImportTypeMapping (TypeTranslator.GetTypeData (type), root,
177 internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
179 return ImportTypeMapping (typeData, (XmlRootAttribute) null,
183 private XmlTypeMapping ImportTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
185 if (typeData == null)
186 throw new ArgumentNullException ("typeData");
188 if (typeData.Type == null)
189 throw new ArgumentException ("Specified TypeData instance does not have Type set.");
191 if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
192 if (defaultNamespace == null) defaultNamespace = string.Empty;
197 switch (typeData.SchemaType) {
198 case SchemaTypes.Class: map = ImportClassMapping (typeData, root, defaultNamespace); break;
199 case SchemaTypes.Array: map = ImportListMapping (typeData, root, defaultNamespace, null, 0); break;
200 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (typeData, root, defaultNamespace); break;
201 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (typeData, root, defaultNamespace); break;
202 case SchemaTypes.Enum: map = ImportEnumMapping (typeData, root, defaultNamespace); break;
203 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (typeData, root, defaultNamespace); break;
204 default: throw new NotSupportedException ("Type " + typeData.Type.FullName + " not supported for XML stialization");
207 map.RelatedMaps = relatedMaps;
208 map.Format = SerializationFormat.Literal;
209 Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
210 map.Source = new XmlTypeSerializationSource (typeData.Type, root, attributeOverrides, defaultNamespace, extraTypes);
211 if (allowPrivateTypes) map.Source.CanBeGenerated = false;
213 } catch (InvalidOperationException ex) {
214 throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
215 "There was an error reflecting type '{0}'.", typeData.Type.FullName), ex);
219 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
221 string rootNamespace = defaultNamespace;
222 string typeNamespace = null;
224 bool includeInSchema = true;
225 XmlAttributes atts = null;
226 bool nullable = true;
228 if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
230 if (!typeData.IsListType)
232 if (attributeOverrides != null)
233 atts = attributeOverrides[typeData.Type];
235 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
236 throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
240 atts = new XmlAttributes (typeData.Type);
242 if (atts.XmlRoot != null && root == null)
245 if (atts.XmlType != null)
247 if (atts.XmlType.Namespace != null && typeData.SchemaType != SchemaTypes.Enum)
248 typeNamespace = atts.XmlType.Namespace;
250 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
251 defaultXmlType = XmlConvert.EncodeLocalName (atts.XmlType.TypeName);
253 includeInSchema = atts.XmlType.IncludeInSchema;
256 elementName = defaultXmlType;
260 if (root.ElementName.Length != 0)
261 elementName = XmlConvert.EncodeLocalName(root.ElementName);
262 if (root.Namespace != null)
263 rootNamespace = root.Namespace;
264 nullable = root.IsNullable;
267 if (rootNamespace == null) rootNamespace = "";
268 if (typeNamespace == null) typeNamespace = rootNamespace;
271 switch (typeData.SchemaType) {
272 case SchemaTypes.XmlSerializable:
273 map = new XmlSerializableMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
275 case SchemaTypes.Primitive:
276 if (!typeData.IsXsdType)
277 map = new XmlTypeMapping (elementName, rootNamespace,
278 typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
280 map = new XmlTypeMapping (elementName, rootNamespace,
281 typeData, defaultXmlType, typeNamespace);
284 map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
288 map.IncludeInSchema = includeInSchema;
289 map.IsNullable = nullable;
290 relatedMaps.Add (map);
295 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
297 TypeData typeData = TypeTranslator.GetTypeData (type);
298 return ImportClassMapping (typeData, root, defaultNamespace);
301 XmlTypeMapping ImportClassMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
303 Type type = typeData.Type;
305 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
306 if (map != null) return map;
308 if (!allowPrivateTypes)
309 ReflectionHelper.CheckSerializableType (type, false);
311 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
312 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
313 helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
317 ClassMap classMap = new ClassMap ();
318 map.ObjectMap = classMap;
320 ICollection members = GetReflectionMembers (type);
321 foreach (XmlReflectionMember rmember in members)
323 string ns = map.XmlTypeNamespace;
324 if (rmember.XmlAttributes.XmlIgnore) continue;
325 if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
326 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
327 ns = bmap.XmlTypeNamespace;
331 XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
332 mem.CheckOptionalValueType (type);
333 classMap.AddMember (mem);
334 } catch (InvalidOperationException ex) {
335 throw new InvalidOperationException (string.Format (
336 CultureInfo.InvariantCulture, "There was an error" +
337 " reflecting field '{0}'.", rmember.MemberName), ex);
341 // Import extra classes
343 if (type == typeof (object) && includedTypes != null)
345 foreach (Type intype in includedTypes)
346 map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
349 // Register inheritance relations
351 if (type.BaseType != null)
353 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
354 ClassMap cbmap = bmap.ObjectMap as ClassMap;
356 if (type.BaseType != typeof (object)) {
358 if (!cbmap.HasSimpleContent)
359 classMap.SetCanBeSimpleType (false);
362 // At this point, derived classes of this map must be already registered
364 RegisterDerivedMap (bmap, map);
366 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
367 throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
370 ImportIncludedTypes (type, defaultNamespace);
372 if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
374 XmlTypeMapMember mem = classMap.XmlTextCollector;
375 if (mem.TypeData.Type != typeof(string) &&
376 mem.TypeData.Type != typeof(string[]) &&
377 mem.TypeData.Type != typeof(object[]) &&
378 mem.TypeData.Type != typeof(XmlNode[]))
380 throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
386 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
388 map.DerivedTypes.Add (derivedMap);
389 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
391 if (map.BaseMap != null)
392 RegisterDerivedMap (map.BaseMap, derivedMap);
394 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
396 obmap.DerivedTypes.Add (derivedMap);
400 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
402 string typeNamespace = null;
404 XmlAttributes atts = null;
405 if (!typeData.IsListType)
407 if (attributeOverrides != null)
408 atts = attributeOverrides[typeData.Type];
412 atts = new XmlAttributes (typeData.Type);
414 if (atts.XmlType != null)
416 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
417 typeNamespace = atts.XmlType.Namespace;
420 if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
422 if (atts.XmlRoot != null && root == null)
427 if (root.Namespace != null && root.Namespace.Length != 0)
428 return root.Namespace;
431 if (defaultNamespace == null) return "";
432 else return defaultNamespace;
435 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
437 TypeData typeData = TypeTranslator.GetTypeData (type);
438 return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel);
441 XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
443 Type type = typeData.Type;
444 ListMap obmap = new ListMap ();
446 if (!allowPrivateTypes)
447 ReflectionHelper.CheckSerializableType (type, true);
449 if (atts == null) atts = new XmlAttributes();
450 Type itemType = typeData.ListItemType;
452 // warning: byte[][] should not be considered multiarray
453 bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
455 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
457 foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
459 if (att.NestingLevel != nestingLevel) continue;
460 Type elemType = (att.Type != null) ? att.Type : itemType;
461 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
462 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
463 if (elem.Namespace == null) elem.Namespace = "";
464 elem.Form = att.Form;
465 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
466 elem.NestingLevel = att.NestingLevel;
469 elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
470 } else if (elem.TypeData.IsComplexType) {
471 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
474 if (att.ElementName.Length != 0) {
475 elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName);
476 } else if (elem.MappedType != null) {
477 elem.ElementName = elem.MappedType.ElementName;
479 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
487 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
489 elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
490 else if (elem.TypeData.IsComplexType)
491 elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
493 if (elem.MappedType != null) {
494 elem.ElementName = elem.MappedType.XmlType;
496 elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType;
499 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
500 elem.IsNullable = CanBeNull (elem.TypeData);
504 obmap.ItemInfo = list;
506 // If there can be different element names (types) in the array, then its name cannot
507 // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
510 if (list.Count > 1) {
511 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
513 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]);
514 if (elem.MappedType != null) {
515 baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
517 baseName = TypeTranslator.GetArrayName (elem.ElementName);
521 // Avoid name colisions
524 string name = baseName;
527 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
528 if (foundMap == null) nameCount = -1;
529 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
530 else name = baseName + (nameCount++);
532 while (nameCount != -1);
534 XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
535 map.ObjectMap = obmap;
537 // Register any of the including types as a derived class of object
538 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
540 XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
541 for (int i = 0; i < includes.Length; i++)
543 Type includedType = includes[i].Type;
544 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
547 // Register this map as a derived class of object
549 helper.RegisterSchemaType (map, name, defaultNamespace);
550 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
555 XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
557 Type type = typeData.Type;
558 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
559 if (map != null) return map;
561 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
562 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
564 if (type.BaseType != null)
566 XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace);
567 if (type.BaseType != typeof (object))
570 RegisterDerivedMap (bmap, map);
576 XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
578 Type type = typeData.Type;
579 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
580 if (map != null) return map;
581 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
582 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
586 XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
588 Type type = typeData.Type;
589 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
590 if (map != null) return map;
592 if (!allowPrivateTypes)
593 ReflectionHelper.CheckSerializableType (type, false);
595 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
596 map.IsNullable = false;
597 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
599 string [] names = Enum.GetNames (type);
600 ArrayList members = new ArrayList();
601 foreach (string name in names)
603 FieldInfo field = type.GetField (name);
604 string xmlName = null;
605 if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
607 object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
608 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
609 if (xmlName == null) xmlName = name;
610 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
611 members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
614 bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
615 map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
616 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
620 XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
622 Type type = typeData.Type;
623 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
624 if (map != null) return map;
626 if (!allowPrivateTypes)
627 ReflectionHelper.CheckSerializableType (type, false);
629 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
630 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
634 void ImportIncludedTypes (Type type, string defaultNamespace)
636 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
637 for (int n=0; n<includes.Length; n++)
639 Type includedType = includes[n].Type;
640 ImportTypeMapping (includedType, null, defaultNamespace);
644 ICollection GetReflectionMembers (Type type)
646 // First we want to find the inheritance hierarchy in reverse order.
647 Type currentType = type;
648 ArrayList typeList = new ArrayList();
649 typeList.Add(currentType);
650 while (currentType != typeof(object))
652 currentType = currentType.BaseType; // Read the base type.
653 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
656 // Read all Fields via reflection.
657 ArrayList fieldList = new ArrayList();
658 FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
660 // This statement ensures fields are ordered starting from the base type.
661 for (int ti=0; ti<typeList.Count; ti++) {
662 for (int i=0; i<tfields.Length; i++) {
663 FieldInfo field = tfields[i];
664 if (field.DeclaringType == typeList[ti])
665 fieldList.Add (field);
670 int currentIndex = 0;
671 foreach (FieldInfo field in tfields)
673 // This statement ensures fields are ordered starting from the base type.
674 if (currentType != field.DeclaringType)
676 currentType = field.DeclaringType;
679 fieldList.Insert(currentIndex++, field);
682 // Read all Properties via reflection.
683 ArrayList propList = new ArrayList();
684 PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
686 // This statement ensures properties are ordered starting from the base type.
687 for (int ti=0; ti<typeList.Count; ti++) {
688 for (int i=0; i<tprops.Length; i++) {
689 PropertyInfo prop = tprops[i];
690 if (!prop.CanRead) continue;
691 if (prop.GetIndexParameters().Length > 0) continue;
692 if (prop.DeclaringType == typeList[ti])
699 foreach (PropertyInfo prop in tprops)
701 // This statement ensures properties are ordered starting from the base type.
702 if (currentType != prop.DeclaringType)
704 currentType = prop.DeclaringType;
707 if (!prop.CanRead) continue;
708 if (prop.GetIndexParameters().Length > 0) continue;
709 propList.Insert(currentIndex++, prop);
712 ArrayList members = new ArrayList();
715 // We now step through the type hierarchy from the base (object) through
716 // to the supplied class, as each step outputting all Fields, and then
717 // all Properties. This is the exact same ordering as .NET 1.0/1.1.
718 foreach (Type t in typeList)
720 // Add any fields matching the current DeclaringType.
721 while (fieldIndex < fieldList.Count)
723 FieldInfo field = (FieldInfo)fieldList[fieldIndex];
724 if (field.DeclaringType==t)
727 XmlAttributes atts = attributeOverrides[type, field.Name];
728 if (atts == null) atts = new XmlAttributes (field);
729 if (atts.XmlIgnore) continue;
730 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
731 member.DeclaringType = field.DeclaringType;
737 // Add any properties matching the current DeclaringType.
738 while (propIndex < propList.Count)
740 PropertyInfo prop = (PropertyInfo)propList[propIndex];
741 if (prop.DeclaringType==t)
744 XmlAttributes atts = attributeOverrides[type, prop.Name];
745 if (atts == null) atts = new XmlAttributes (prop);
746 if (atts.XmlIgnore) continue;
747 if (!prop.CanWrite && (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray)) continue;
748 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
749 member.DeclaringType = prop.DeclaringType;
758 private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace)
760 XmlTypeMapMember mapMember;
761 XmlAttributes atts = rmember.XmlAttributes;
762 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
764 if (atts.XmlAnyAttribute != null)
766 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
767 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
769 mapMember = new XmlTypeMapMemberAnyAttribute();
772 throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
774 else if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
776 if ( (rmember.MemberType.FullName == "System.Xml.XmlElement[]") ||
777 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") ||
778 (rmember.MemberType.FullName == "System.Xml.XmlElement"))
780 XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
781 member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
785 throw new InvalidOperationException ("XmlAnyElementAttribute can only be applied to members of type XmlElement, XmlElement[] or XmlNode[]");
789 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
790 mapMember = mapNamespaces;
792 else if (atts.XmlAttribute != null)
796 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
797 throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
799 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
800 if (atts.XmlAttribute.AttributeName.Length == 0)
801 mapAttribute.AttributeName = rmember.MemberName;
803 mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
805 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (mapAttribute.AttributeName);
807 if (typeData.IsComplexType)
808 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, mapAttribute.Namespace);
810 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
812 if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
813 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
814 mapAttribute.Form = XmlSchemaForm.Qualified;
815 mapAttribute.Namespace = atts.XmlAttribute.Namespace;
819 mapAttribute.Form = atts.XmlAttribute.Form;
820 if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
821 mapAttribute.Namespace = defaultNamespace;
823 mapAttribute.Namespace = "";
826 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
827 mapMember = mapAttribute;
829 else if (typeData.SchemaType == SchemaTypes.Array)
831 // If the member has a single XmlElementAttribute and the type is the type of the member,
832 // then it is not a flat list
834 if (atts.XmlElements.Count > 1 ||
835 (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
836 (atts.XmlText != null))
840 // TODO: check that it does not have XmlArrayAttribute
841 XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
842 member.ListMap = new ListMap ();
843 member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts);
844 member.ElementInfo = member.ListMap.ItemInfo;
845 member.ListMap.ChoiceMember = member.ChoiceMember;
852 XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
854 // Creates an ElementInfo that identifies the array instance.
855 member.ElementInfo = new XmlTypeMapElementInfoList();
856 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
857 elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName);
858 elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
859 elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
860 elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
861 elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
863 member.ElementInfo.Add (elem);
871 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
872 member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts);
876 mapMember.DefaultValue = atts.XmlDefaultValue;
877 mapMember.TypeData = typeData;
878 mapMember.Name = rmember.MemberName;
879 mapMember.IsReturnValue = rmember.IsReturnValue;
883 XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
885 EnumMap choiceEnumMap = null;
886 Type choiceEnumType = null;
888 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
889 ImportTextElementInfo (list, defaultType, member, atts);
891 if (atts.XmlChoiceIdentifier != null) {
893 throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context.");
895 member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName;
896 MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public);
898 if (mems.Length == 0)
899 throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls);
901 if (mems[0] is PropertyInfo) {
902 PropertyInfo pi = (PropertyInfo)mems[0];
903 if (!pi.CanWrite || !pi.CanRead)
904 throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write.");
905 choiceEnumType = pi.PropertyType;
907 else choiceEnumType = ((FieldInfo)mems[0]).FieldType;
909 member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType);
911 if (choiceEnumType.IsArray)
912 choiceEnumType = choiceEnumType.GetElementType ();
914 choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap;
915 if (choiceEnumMap == null)
916 throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute.");
919 if (atts.XmlElements.Count == 0 && list.Count == 0)
921 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
922 elem.ElementName = defaultName;
923 elem.Namespace = defaultNamespace;
924 if (elem.TypeData.IsComplexType)
925 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
929 bool multiType = (atts.XmlElements.Count > 1);
930 foreach (XmlElementAttribute att in atts.XmlElements)
932 Type elemType = (att.Type != null) ? att.Type : defaultType;
933 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
934 elem.Form = att.Form;
935 if (elem.Form != XmlSchemaForm.Unqualified)
936 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
937 elem.IsNullable = att.IsNullable;
939 if (elem.IsNullable && elem.TypeData.IsValueType)
940 throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
942 if (elem.TypeData.IsComplexType)
944 if (att.DataType.Length != 0) throw new InvalidOperationException (
945 string.Format(CultureInfo.InvariantCulture, "'{0}' is "
946 + "an invalid value for '{1}.{2}' of type '{3}'. "
947 + "The property may only be specified for primitive types.",
948 att.DataType, cls.FullName, defaultName,
949 elem.TypeData.FullTypeName));
950 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
953 if (att.ElementName.Length != 0) {
954 elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName);
955 } else if (multiType) {
956 if (elem.MappedType != null) {
957 elem.ElementName = elem.MappedType.ElementName;
959 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
962 elem.ElementName = defaultName;
965 if (choiceEnumMap != null) {
966 string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
968 throw new InvalidOperationException (string.Format (
969 CultureInfo.InvariantCulture, "Type {0} is missing"
970 + " enumeration value '{1}' for element '{1} from"
971 + " namespace '{2}'.", choiceEnumType, elem.ElementName,
973 elem.ChoiceValue = Enum.Parse (choiceEnumType, cname);
981 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
983 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
985 ImportTextElementInfo (list, rmember.MemberType, member, atts);
987 foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
989 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
990 if (att.Name.Length != 0)
992 elem.ElementName = XmlConvert.EncodeLocalName(att.Name);
993 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
997 elem.IsUnnamedAnyElement = true;
998 elem.Namespace = defaultNamespace;
999 if (att.Namespace != null)
1000 throw new InvalidOperationException ("The element " + rmember.MemberName + " has been attributed with an XmlAnyElementAttribute and a namespace '" + att.Namespace + "', but no name. When a namespace is supplied, a name is also required. Supply a name or remove the namespace.");
1007 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
1009 if (atts.XmlText != null)
1011 member.IsXmlTextCollector = true;
1012 if (atts.XmlText.Type != null) {
1013 TypeData td = TypeTranslator.GetTypeData (defaultType);
1014 if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
1015 throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
1017 defaultType = atts.XmlText.Type;
1019 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes
1021 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
1023 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
1024 elem.TypeData.SchemaType != SchemaTypes.Enum &&
1025 elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
1026 !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
1028 throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
1030 elem.IsTextElement = true;
1031 elem.WrappedElement = false;
1036 bool CanBeNull (TypeData type)
1038 return (type.SchemaType != SchemaTypes.Primitive || type.Type == typeof (string));
1041 public void IncludeType (Type type)
1044 throw new ArgumentNullException ("type");
1046 if (includedTypes == null) includedTypes = new ArrayList ();
1047 if (!includedTypes.Contains (type))
1048 includedTypes.Add (type);
1050 if (relatedMaps.Count > 0) {
1051 foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) {
1052 if (map.TypeData.Type == typeof(object))
1053 map.DerivedTypes.Add (ImportTypeMapping (type));
1058 public void IncludeTypes (ICustomAttributeProvider provider)
1060 object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
1062 foreach (XmlIncludeAttribute at in ats)
1063 IncludeType (at.Type);
1066 #endregion // Methods