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 if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
174 if (defaultNamespace == null) defaultNamespace = string.Empty;
179 switch (TypeTranslator.GetTypeData (type).SchemaType) {
180 case SchemaTypes.Class: map = ImportClassMapping (type, root, defaultNamespace); break;
181 case SchemaTypes.Array: map = ImportListMapping (type, root, defaultNamespace, null, 0); break;
182 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (type, root, defaultNamespace); break;
183 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (type, root, defaultNamespace); break;
184 case SchemaTypes.Enum: map = ImportEnumMapping (type, root, defaultNamespace); break;
185 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (type, root, defaultNamespace); break;
186 default: throw new NotSupportedException ("Type " + type.FullName + " not supported for XML stialization");
189 map.RelatedMaps = relatedMaps;
190 map.Format = SerializationFormat.Literal;
191 Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
192 map.Source = new XmlTypeSerializationSource (type, root, attributeOverrides, defaultNamespace, extraTypes);
193 if (allowPrivateTypes) map.Source.CanBeGenerated = false;
195 } catch (InvalidOperationException ex) {
196 throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
197 "There was an error reflecting type '{0}'.", type.FullName), ex);
201 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
203 string rootNamespace = defaultNamespace;
204 string typeNamespace = null;
206 bool includeInSchema = true;
207 XmlAttributes atts = null;
208 bool nullable = true;
210 if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
212 if (!typeData.IsListType)
214 if (attributeOverrides != null)
215 atts = attributeOverrides[typeData.Type];
217 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
218 throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
222 atts = new XmlAttributes (typeData.Type);
224 if (atts.XmlRoot != null && root == null)
227 if (atts.XmlType != null)
229 if (atts.XmlType.Namespace != null && typeData.SchemaType != SchemaTypes.Enum)
230 typeNamespace = atts.XmlType.Namespace;
232 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
233 defaultXmlType = XmlConvert.EncodeLocalName (atts.XmlType.TypeName);
235 includeInSchema = atts.XmlType.IncludeInSchema;
238 elementName = defaultXmlType;
242 if (root.ElementName.Length != 0)
243 elementName = XmlConvert.EncodeLocalName(root.ElementName);
244 if (root.Namespace != null)
245 rootNamespace = root.Namespace;
246 nullable = root.IsNullable;
249 if (rootNamespace == null) rootNamespace = "";
250 if (typeNamespace == null) typeNamespace = rootNamespace;
253 switch (typeData.SchemaType) {
254 case SchemaTypes.XmlSerializable:
255 map = new XmlSerializableMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
257 case SchemaTypes.Primitive:
258 if (!typeData.IsXsdType)
259 map = new XmlTypeMapping (elementName, rootNamespace,
260 typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
262 map = new XmlTypeMapping (elementName, rootNamespace,
263 typeData, defaultXmlType, typeNamespace);
266 map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
270 map.IncludeInSchema = includeInSchema;
271 map.IsNullable = nullable;
272 relatedMaps.Add (map);
277 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
279 TypeData typeData = TypeTranslator.GetTypeData (type);
280 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
281 if (map != null) return map;
283 if (!allowPrivateTypes)
284 ReflectionHelper.CheckSerializableType (type, false);
286 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
287 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
288 helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
292 ClassMap classMap = new ClassMap ();
293 map.ObjectMap = classMap;
295 ICollection members = GetReflectionMembers (type);
296 foreach (XmlReflectionMember rmember in members)
298 string ns = map.XmlTypeNamespace;
299 if (rmember.XmlAttributes.XmlIgnore) continue;
300 if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
301 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
302 ns = bmap.XmlTypeNamespace;
306 XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
307 mem.CheckOptionalValueType (type);
308 classMap.AddMember (mem);
309 } catch (InvalidOperationException ex) {
310 throw new InvalidOperationException (string.Format (
311 CultureInfo.InvariantCulture, "There was an error" +
312 " reflecting field '{0}'.", rmember.MemberName), ex);
316 // Import extra classes
318 if (type == typeof (object) && includedTypes != null)
320 foreach (Type intype in includedTypes)
321 map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
324 // Register inheritance relations
326 if (type.BaseType != null)
328 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
329 ClassMap cbmap = bmap.ObjectMap as ClassMap;
331 if (type.BaseType != typeof (object)) {
333 if (!cbmap.HasSimpleContent)
334 classMap.SetCanBeSimpleType (false);
337 // At this point, derived classes of this map must be already registered
339 RegisterDerivedMap (bmap, map);
341 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
342 throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
345 ImportIncludedTypes (type, defaultNamespace);
347 if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
349 XmlTypeMapMember mem = classMap.XmlTextCollector;
350 if (mem.TypeData.Type != typeof(string) &&
351 mem.TypeData.Type != typeof(string[]) &&
352 mem.TypeData.Type != typeof(object[]) &&
353 mem.TypeData.Type != typeof(XmlNode[]))
355 throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
361 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
363 map.DerivedTypes.Add (derivedMap);
364 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
366 if (map.BaseMap != null)
367 RegisterDerivedMap (map.BaseMap, derivedMap);
369 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
371 obmap.DerivedTypes.Add (derivedMap);
375 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
377 string typeNamespace = null;
379 XmlAttributes atts = null;
380 if (!typeData.IsListType)
382 if (attributeOverrides != null)
383 atts = attributeOverrides[typeData.Type];
387 atts = new XmlAttributes (typeData.Type);
389 if (atts.XmlType != null)
391 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
392 typeNamespace = atts.XmlType.Namespace;
395 if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
397 if (atts.XmlRoot != null && root == null)
402 if (root.Namespace != null && root.Namespace.Length != 0)
403 return root.Namespace;
406 if (defaultNamespace == null) return "";
407 else return defaultNamespace;
410 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
412 TypeData typeData = TypeTranslator.GetTypeData (type);
413 ListMap obmap = new ListMap ();
415 if (!allowPrivateTypes)
416 ReflectionHelper.CheckSerializableType (type, true);
418 if (atts == null) atts = new XmlAttributes();
419 Type itemType = typeData.ListItemType;
421 // warning: byte[][] should not be considered multiarray
422 bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
424 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
426 foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
428 if (att.NestingLevel != nestingLevel) continue;
429 Type elemType = (att.Type != null) ? att.Type : itemType;
430 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
431 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
432 if (elem.Namespace == null) elem.Namespace = "";
433 elem.Form = att.Form;
434 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
435 elem.NestingLevel = att.NestingLevel;
438 elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
439 } else if (elem.TypeData.IsComplexType) {
440 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
443 if (att.ElementName.Length != 0) {
444 elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName);
445 } else if (elem.MappedType != null) {
446 elem.ElementName = elem.MappedType.ElementName;
448 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
456 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
458 elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
459 else if (elem.TypeData.IsComplexType)
460 elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
462 if (elem.MappedType != null) {
463 elem.ElementName = elem.MappedType.XmlType;
465 elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType;
468 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
469 elem.IsNullable = CanBeNull (elem.TypeData);
473 obmap.ItemInfo = list;
475 // If there can be different element names (types) in the array, then its name cannot
476 // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
479 if (list.Count > 1) {
480 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
482 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]);
483 if (elem.MappedType != null) {
484 baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
486 baseName = TypeTranslator.GetArrayName (elem.ElementName);
490 // Avoid name colisions
493 string name = baseName;
496 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
497 if (foundMap == null) nameCount = -1;
498 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
499 else name = baseName + (nameCount++);
501 while (nameCount != -1);
503 XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
504 map.ObjectMap = obmap;
506 // Register any of the including types as a derived class of object
507 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
509 XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
510 for (int i = 0; i < includes.Length; i++)
512 Type includedType = includes[i].Type;
513 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
516 // Register this map as a derived class of object
518 helper.RegisterSchemaType (map, name, defaultNamespace);
519 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
524 XmlTypeMapping ImportXmlNodeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
526 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (TypeTranslator.GetTypeData (type), root, defaultNamespace));
527 if (map != null) return map;
529 map = CreateTypeMapping (TypeTranslator.GetTypeData (type), root, null, defaultNamespace);
530 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
532 if (type.BaseType != null)
534 XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace);
535 if (type.BaseType != typeof (object))
538 RegisterDerivedMap (bmap, map);
544 XmlTypeMapping ImportPrimitiveMapping (Type type, XmlRootAttribute root, string defaultNamespace)
546 TypeData typeData = TypeTranslator.GetTypeData (type);
547 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
548 if (map != null) return map;
549 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
550 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
554 XmlTypeMapping ImportEnumMapping (Type type, XmlRootAttribute root, string defaultNamespace)
556 TypeData typeData = TypeTranslator.GetTypeData (type);
557 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
558 if (map != null) return map;
560 if (!allowPrivateTypes)
561 ReflectionHelper.CheckSerializableType (type, false);
563 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
564 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
566 string [] names = Enum.GetNames (type);
567 ArrayList members = new ArrayList();
568 foreach (string name in names)
570 FieldInfo field = type.GetField (name);
571 string xmlName = null;
572 if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
574 object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
575 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
576 if (xmlName == null) xmlName = name;
577 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
578 members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
581 bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
582 map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
583 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
587 XmlTypeMapping ImportXmlSerializableMapping (Type type, XmlRootAttribute root, string defaultNamespace)
589 TypeData typeData = TypeTranslator.GetTypeData (type);
590 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
591 if (map != null) return map;
593 if (!allowPrivateTypes)
594 ReflectionHelper.CheckSerializableType (type, false);
596 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
597 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
601 void ImportIncludedTypes (Type type, string defaultNamespace)
603 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
604 for (int n=0; n<includes.Length; n++)
606 Type includedType = includes[n].Type;
607 ImportTypeMapping (includedType, null, defaultNamespace);
611 ICollection GetReflectionMembers (Type type)
613 // First we want to find the inheritance hierarchy in reverse order.
614 Type currentType = type;
615 ArrayList typeList = new ArrayList();
616 typeList.Add(currentType);
617 while (currentType != typeof(object))
619 currentType = currentType.BaseType; // Read the base type.
620 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
623 // Read all Fields via reflection.
624 ArrayList fieldList = new ArrayList();
625 FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
627 // This statement ensures fields are ordered starting from the base type.
628 for (int ti=0; ti<typeList.Count; ti++) {
629 for (int i=0; i<tfields.Length; i++) {
630 FieldInfo field = tfields[i];
631 if (field.DeclaringType == typeList[ti])
632 fieldList.Add (field);
637 int currentIndex = 0;
638 foreach (FieldInfo field in tfields)
640 // This statement ensures fields are ordered starting from the base type.
641 if (currentType != field.DeclaringType)
643 currentType = field.DeclaringType;
646 fieldList.Insert(currentIndex++, field);
649 // Read all Properties via reflection.
650 ArrayList propList = new ArrayList();
651 PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
653 // This statement ensures properties are ordered starting from the base type.
654 for (int ti=0; ti<typeList.Count; ti++) {
655 for (int i=0; i<tprops.Length; i++) {
656 PropertyInfo prop = tprops[i];
657 if (!prop.CanRead) continue;
658 if (prop.GetIndexParameters().Length > 0) continue;
659 if (prop.DeclaringType == typeList[ti])
666 foreach (PropertyInfo prop in tprops)
668 // This statement ensures properties are ordered starting from the base type.
669 if (currentType != prop.DeclaringType)
671 currentType = prop.DeclaringType;
674 if (!prop.CanRead) continue;
675 if (prop.GetIndexParameters().Length > 0) continue;
676 propList.Insert(currentIndex++, prop);
679 ArrayList members = new ArrayList();
682 // We now step through the type hierarchy from the base (object) through
683 // to the supplied class, as each step outputting all Fields, and then
684 // all Properties. This is the exact same ordering as .NET 1.0/1.1.
685 foreach (Type t in typeList)
687 // Add any fields matching the current DeclaringType.
688 while (fieldIndex < fieldList.Count)
690 FieldInfo field = (FieldInfo)fieldList[fieldIndex];
691 if (field.DeclaringType==t)
694 XmlAttributes atts = attributeOverrides[type, field.Name];
695 if (atts == null) atts = new XmlAttributes (field);
696 if (atts.XmlIgnore) continue;
697 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
698 member.DeclaringType = field.DeclaringType;
704 // Add any properties matching the current DeclaringType.
705 while (propIndex < propList.Count)
707 PropertyInfo prop = (PropertyInfo)propList[propIndex];
708 if (prop.DeclaringType==t)
711 XmlAttributes atts = attributeOverrides[type, prop.Name];
712 if (atts == null) atts = new XmlAttributes (prop);
713 if (atts.XmlIgnore) continue;
714 if (!prop.CanWrite && (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray)) continue;
715 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
716 member.DeclaringType = prop.DeclaringType;
725 private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace)
727 XmlTypeMapMember mapMember;
728 XmlAttributes atts = rmember.XmlAttributes;
729 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
731 if (atts.XmlAnyAttribute != null)
733 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
734 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
736 mapMember = new XmlTypeMapMemberAnyAttribute();
739 throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
741 else if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
743 if ( (rmember.MemberType.FullName == "System.Xml.XmlElement[]") ||
744 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") ||
745 (rmember.MemberType.FullName == "System.Xml.XmlElement"))
747 XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
748 member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
752 throw new InvalidOperationException ("XmlAnyElementAttribute can only be applied to members of type XmlElement, XmlElement[] or XmlNode[]");
756 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
757 mapMember = mapNamespaces;
759 else if (atts.XmlAttribute != null)
763 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
764 throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
766 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
767 if (atts.XmlAttribute.AttributeName.Length == 0)
768 mapAttribute.AttributeName = rmember.MemberName;
770 mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
772 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (mapAttribute.AttributeName);
774 if (typeData.IsComplexType)
775 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, mapAttribute.Namespace);
777 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
779 if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
780 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
781 mapAttribute.Form = XmlSchemaForm.Qualified;
782 mapAttribute.Namespace = atts.XmlAttribute.Namespace;
786 mapAttribute.Form = atts.XmlAttribute.Form;
787 if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
788 mapAttribute.Namespace = defaultNamespace;
790 mapAttribute.Namespace = "";
793 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
794 mapMember = mapAttribute;
796 else if (typeData.SchemaType == SchemaTypes.Array)
798 // If the member has a single XmlElementAttribute and the type is the type of the member,
799 // then it is not a flat list
801 if (atts.XmlElements.Count > 1 ||
802 (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
803 (atts.XmlText != null))
807 // TODO: check that it does not have XmlArrayAttribute
808 XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
809 member.ListMap = new ListMap ();
810 member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts);
811 member.ElementInfo = member.ListMap.ItemInfo;
812 member.ListMap.ChoiceMember = member.ChoiceMember;
819 XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
821 // Creates an ElementInfo that identifies the array instance.
822 member.ElementInfo = new XmlTypeMapElementInfoList();
823 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
824 elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName);
825 elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
826 elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
827 elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
828 elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
830 member.ElementInfo.Add (elem);
838 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
839 member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts);
843 mapMember.DefaultValue = atts.XmlDefaultValue;
844 mapMember.TypeData = typeData;
845 mapMember.Name = rmember.MemberName;
846 mapMember.IsReturnValue = rmember.IsReturnValue;
850 XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
852 EnumMap choiceEnumMap = null;
853 Type choiceEnumType = null;
855 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
856 ImportTextElementInfo (list, defaultType, member, atts);
858 if (atts.XmlChoiceIdentifier != null) {
860 throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context.");
862 member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName;
863 MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public);
865 if (mems.Length == 0)
866 throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls);
868 if (mems[0] is PropertyInfo) {
869 PropertyInfo pi = (PropertyInfo)mems[0];
870 if (!pi.CanWrite || !pi.CanRead)
871 throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write.");
872 choiceEnumType = pi.PropertyType;
874 else choiceEnumType = ((FieldInfo)mems[0]).FieldType;
876 member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType);
878 if (choiceEnumType.IsArray)
879 choiceEnumType = choiceEnumType.GetElementType ();
881 choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap;
882 if (choiceEnumMap == null)
883 throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute.");
886 if (atts.XmlElements.Count == 0 && list.Count == 0)
888 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
889 elem.ElementName = defaultName;
890 elem.Namespace = defaultNamespace;
891 if (elem.TypeData.IsComplexType)
892 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
896 bool multiType = (atts.XmlElements.Count > 1);
897 foreach (XmlElementAttribute att in atts.XmlElements)
899 Type elemType = (att.Type != null) ? att.Type : defaultType;
900 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
901 elem.Form = att.Form;
902 if (elem.Form != XmlSchemaForm.Unqualified)
903 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
904 elem.IsNullable = att.IsNullable;
906 if (elem.IsNullable && elem.TypeData.IsValueType)
907 throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
909 if (elem.TypeData.IsComplexType)
911 if (att.DataType.Length != 0) throw new InvalidOperationException (
912 string.Format(CultureInfo.InvariantCulture, "'{0}' is "
913 + "an invalid value for '{1}.{2}' of type '{3}'. "
914 + "The property may only be specified for primitive types.",
915 att.DataType, cls.FullName, defaultName,
916 elem.TypeData.FullTypeName));
917 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
920 if (att.ElementName.Length != 0) {
921 elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName);
922 } else if (multiType) {
923 if (elem.MappedType != null) {
924 elem.ElementName = elem.MappedType.ElementName;
926 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
929 elem.ElementName = defaultName;
932 if (choiceEnumMap != null) {
933 string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
935 throw new InvalidOperationException (string.Format (
936 CultureInfo.InvariantCulture, "Type {0} is missing"
937 + " enumeration value '{1}' for element '{1} from"
938 + " namespace '{2}'.", choiceEnumType, elem.ElementName,
940 elem.ChoiceValue = Enum.Parse (choiceEnumType, cname);
948 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
950 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
952 ImportTextElementInfo (list, rmember.MemberType, member, atts);
954 foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
956 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
957 if (att.Name.Length != 0)
959 elem.ElementName = XmlConvert.EncodeLocalName(att.Name);
960 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
964 elem.IsUnnamedAnyElement = true;
965 elem.Namespace = defaultNamespace;
966 if (att.Namespace != null)
967 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.");
974 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
976 if (atts.XmlText != null)
978 member.IsXmlTextCollector = true;
979 if (atts.XmlText.Type != null) {
980 TypeData td = TypeTranslator.GetTypeData (defaultType);
981 if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
982 throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
984 defaultType = atts.XmlText.Type;
986 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes
988 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
990 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
991 elem.TypeData.SchemaType != SchemaTypes.Enum &&
992 elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
993 !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
995 throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
997 elem.IsTextElement = true;
998 elem.WrappedElement = false;
1003 bool CanBeNull (TypeData type)
1005 return (type.SchemaType != SchemaTypes.Primitive || type.Type == typeof (string));
1008 public void IncludeType (Type type)
1011 throw new ArgumentNullException ("type");
1013 if (includedTypes == null) includedTypes = new ArrayList ();
1014 if (!includedTypes.Contains (type))
1015 includedTypes.Add (type);
1017 if (relatedMaps.Count > 0) {
1018 foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) {
1019 if (map.TypeData.Type == typeof(object))
1020 map.DerivedTypes.Add (ImportTypeMapping (type));
1025 public void IncludeTypes (ICustomAttributeProvider provider)
1027 object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
1029 foreach (XmlIncludeAttribute at in ats)
1030 IncludeType (at.Type);
1033 #endregion // Methods