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.Collections.Generic;
36 using System.Globalization;
37 using System.Reflection;
38 using System.Xml.Schema;
40 namespace System.Xml.Serialization {
41 public class XmlReflectionImporter {
43 string initialDefaultNamespace;
44 XmlAttributeOverrides attributeOverrides;
45 ArrayList includedTypes;
46 ReflectionHelper helper = new ReflectionHelper();
47 int arrayChoiceCount = 1;
48 ArrayList relatedMaps = new ArrayList ();
49 bool allowPrivateTypes = false;
51 static readonly string errSimple = "Cannot serialize object of type '{0}'. Base " +
52 "type '{1}' has simpleContent and can be only extended by adding XmlAttribute " +
53 "elements. Please consider changing XmlText member of the base class to string array";
55 static readonly string errSimple2 = "Cannot serialize object of type '{0}'. " +
56 "Consider changing type of XmlText member '{1}' from '{2}' to string or string array";
60 public XmlReflectionImporter ()
65 public XmlReflectionImporter (string defaultNamespace)
66 : this (null, defaultNamespace)
70 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides)
71 : this (attributeOverrides, null)
75 public XmlReflectionImporter (XmlAttributeOverrides attributeOverrides, string defaultNamespace)
77 if (defaultNamespace == null)
78 this.initialDefaultNamespace = String.Empty;
80 this.initialDefaultNamespace = defaultNamespace;
82 if (attributeOverrides == null)
83 this.attributeOverrides = new XmlAttributeOverrides();
85 this.attributeOverrides = attributeOverrides;
90 helper = new ReflectionHelper();
95 internal bool AllowPrivateTypes
97 get { return allowPrivateTypes; }
98 set { allowPrivateTypes = value; }
101 #endregion // Constructors
105 public XmlMembersMapping ImportMembersMapping (string elementName,
107 XmlReflectionMember [] members,
108 bool hasWrapperElement)
110 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, true);
117 XmlMembersMapping ImportMembersMapping (string elementName,
119 XmlReflectionMember[] members,
120 bool hasWrapperElement,
123 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, rpc, true);
130 XmlMembersMapping ImportMembersMapping (string elementName,
132 XmlReflectionMember[] members,
133 bool hasWrapperElement,
137 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, rpc, openModel, XmlMappingAccess.Read | XmlMappingAccess.Write);
141 [MonoTODO] // FIXME: handle writeAccessors, validate, and mapping access
144 XmlMembersMapping ImportMembersMapping (string elementName,
146 XmlReflectionMember[] members,
147 bool hasWrapperElement,
150 XmlMappingAccess access)
152 // Reset (); Disabled. See ChangeLog
154 ArrayList mapping = new ArrayList ();
155 for (int n=0; n<members.Length; n++)
157 XmlTypeMapMember mapMem = CreateMapMember (null, members[n], ns);
158 mapMem.GlobalIndex = n;
159 mapMem.CheckOptionalValueType (members);
160 mapping.Add (new XmlMemberMapping (members[n].MemberName, ns, mapMem, false));
162 elementName = XmlConvert.EncodeLocalName (elementName);
163 XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, (XmlMemberMapping[])mapping.ToArray (typeof(XmlMemberMapping)));
164 mps.RelatedMaps = relatedMaps;
165 mps.Format = SerializationFormat.Literal;
166 Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
168 mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
169 if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
174 public XmlTypeMapping ImportTypeMapping (Type type)
176 return ImportTypeMapping (type, null, null);
179 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
181 return ImportTypeMapping (type, null, defaultNamespace);
184 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root)
186 return ImportTypeMapping (type, root, null);
189 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
192 throw new ArgumentNullException ("type");
194 if (type == typeof (void))
195 throw new NotSupportedException ("The type " + type.FullName + " may not be serialized.");
197 return ImportTypeMapping (TypeTranslator.GetTypeData (type), root,
201 internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
203 return ImportTypeMapping (typeData, (XmlRootAttribute) null,
207 private XmlTypeMapping ImportTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
209 if (typeData == null)
210 throw new ArgumentNullException ("typeData");
212 if (typeData.Type == null)
213 throw new ArgumentException ("Specified TypeData instance does not have Type set.");
215 if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
216 if (defaultNamespace == null) defaultNamespace = string.Empty;
221 switch (typeData.SchemaType) {
222 case SchemaTypes.Class: map = ImportClassMapping (typeData, root, defaultNamespace); break;
223 case SchemaTypes.Array: map = ImportListMapping (typeData, root, defaultNamespace, null, 0); break;
224 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (typeData, root, defaultNamespace); break;
225 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (typeData, root, defaultNamespace); break;
226 case SchemaTypes.Enum: map = ImportEnumMapping (typeData, root, defaultNamespace); break;
227 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (typeData, root, defaultNamespace); break;
228 default: throw new NotSupportedException ("Type " + typeData.Type.FullName + " not supported for XML stialization");
233 map.SetKey (typeData.Type.ToString ());
235 map.RelatedMaps = relatedMaps;
236 map.Format = SerializationFormat.Literal;
237 Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
239 map.Source = new XmlTypeSerializationSource (typeData.Type, root, attributeOverrides, defaultNamespace, extraTypes);
240 if (allowPrivateTypes) map.Source.CanBeGenerated = false;
243 } catch (InvalidOperationException ex) {
244 throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
245 "There was an error reflecting type '{0}'.", typeData.Type.FullName), ex);
249 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
251 string rootNamespace = defaultNamespace;
252 string typeNamespace = null;
254 bool includeInSchema = true;
255 XmlAttributes atts = null;
256 bool nullable = CanBeNull (typeData);
258 if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
260 if (!typeData.IsListType)
262 if (attributeOverrides != null)
263 atts = attributeOverrides[typeData.Type];
265 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
266 throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
270 atts = new XmlAttributes (typeData.Type);
272 if (atts.XmlRoot != null && root == null)
275 if (atts.XmlType != null)
277 if (atts.XmlType.Namespace != null)
278 typeNamespace = atts.XmlType.Namespace;
280 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
281 defaultXmlType = XmlConvert.EncodeLocalName (atts.XmlType.TypeName);
283 includeInSchema = atts.XmlType.IncludeInSchema;
286 elementName = defaultXmlType;
290 if (root.ElementName.Length != 0)
291 elementName = XmlConvert.EncodeLocalName(root.ElementName);
292 if (root.Namespace != null)
293 rootNamespace = root.Namespace;
294 nullable = root.IsNullable;
297 if (rootNamespace == null) rootNamespace = "";
298 if (typeNamespace == null) typeNamespace = rootNamespace;
301 switch (typeData.SchemaType) {
302 case SchemaTypes.XmlSerializable:
303 map = new XmlSerializableMapping (root, elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
305 case SchemaTypes.Primitive:
306 if (!typeData.IsXsdType)
307 map = new XmlTypeMapping (elementName, rootNamespace,
308 typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
310 map = new XmlTypeMapping (elementName, rootNamespace,
311 typeData, defaultXmlType, typeNamespace);
314 map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
318 map.IncludeInSchema = includeInSchema;
319 map.IsNullable = nullable;
320 relatedMaps.Add (map);
325 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
327 TypeData typeData = TypeTranslator.GetTypeData (type);
328 return ImportClassMapping (typeData, root, defaultNamespace);
331 XmlTypeMapping ImportClassMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
333 Type type = typeData.Type;
335 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
336 if (map != null) return map;
338 if (!allowPrivateTypes)
339 ReflectionHelper.CheckSerializableType (type, false);
341 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
342 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
343 helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
347 ClassMap classMap = new ClassMap ();
348 map.ObjectMap = classMap;
350 var members = GetReflectionMembers (type);
351 bool? isOrderExplicit = null;
352 foreach (XmlReflectionMember rmember in members)
354 int? order = rmember.XmlAttributes.Order;
355 if (isOrderExplicit == null)
358 isOrderExplicit = (int) order >= 0;
360 else if (order != null && isOrderExplicit != ((int) order >= 0))
361 throw new InvalidOperationException ("Inconsistent XML sequence was detected. If there are XmlElement/XmlArray/XmlAnyElement attributes with explicit Order, then every other member must have an explicit order too.");
363 if (isOrderExplicit == true)
364 members.Sort ((m1, m2) => (int) m1.XmlAttributes.SortableOrder - (int) m2.XmlAttributes.SortableOrder);
366 foreach (XmlReflectionMember rmember in members)
368 string ns = map.XmlTypeNamespace;
369 if (rmember.XmlAttributes.XmlIgnore) continue;
370 if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
371 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
372 ns = bmap.XmlTypeNamespace;
376 XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
377 mem.CheckOptionalValueType (type);
378 classMap.AddMember (mem);
379 } catch (Exception ex) {
380 throw new InvalidOperationException (string.Format (
381 CultureInfo.InvariantCulture, "There was an error" +
382 " reflecting field '{0}'.", rmember.MemberName), ex);
386 // Import extra classes
388 if (type == typeof (object) && includedTypes != null)
390 foreach (Type intype in includedTypes)
391 map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
394 // Register inheritance relations
396 if (type.BaseType != null)
398 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
399 ClassMap cbmap = bmap.ObjectMap as ClassMap;
401 if (type.BaseType != typeof (object)) {
403 if (!cbmap.HasSimpleContent)
404 classMap.SetCanBeSimpleType (false);
407 // At this point, derived classes of this map must be already registered
409 RegisterDerivedMap (bmap, map);
411 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
412 throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
415 ImportIncludedTypes (type, defaultNamespace);
417 if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
419 XmlTypeMapMember mem = classMap.XmlTextCollector;
420 if (mem.TypeData.Type != typeof(string) &&
421 mem.TypeData.Type != typeof(string[]) &&
423 mem.TypeData.Type != typeof(XmlNode[]) &&
425 mem.TypeData.Type != typeof(object[]))
427 throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
433 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
435 map.DerivedTypes.Add (derivedMap);
436 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
438 if (map.BaseMap != null)
439 RegisterDerivedMap (map.BaseMap, derivedMap);
441 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
443 obmap.DerivedTypes.Add (derivedMap);
447 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
449 string typeNamespace = null;
451 XmlAttributes atts = null;
452 if (!typeData.IsListType)
454 if (attributeOverrides != null)
455 atts = attributeOverrides[typeData.Type];
459 atts = new XmlAttributes (typeData.Type);
461 if (atts.XmlType != null)
463 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
464 typeNamespace = atts.XmlType.Namespace;
467 if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
469 if (atts.XmlRoot != null && root == null)
474 if (root.Namespace != null && root.Namespace.Length != 0)
475 return root.Namespace;
478 if (defaultNamespace == null) return "";
479 else return defaultNamespace;
482 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
484 TypeData typeData = TypeTranslator.GetTypeData (type);
485 return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel);
488 XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
490 Type type = typeData.Type;
491 ListMap obmap = new ListMap ();
493 if (!allowPrivateTypes)
494 ReflectionHelper.CheckSerializableType (type, true);
496 if (atts == null) atts = new XmlAttributes();
497 Type itemType = typeData.ListItemType;
499 // warning: byte[][] should not be considered multiarray
500 bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
502 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
504 foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
506 if (att.Namespace != null && att.Form == XmlSchemaForm.Unqualified)
507 throw new InvalidOperationException ("XmlArrayItemAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
508 if (att.NestingLevel != nestingLevel) continue;
509 Type elemType = (att.Type != null) ? att.Type : itemType;
510 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
511 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
512 if (elem.Namespace == null) elem.Namespace = "";
513 elem.Form = att.Form;
514 if (att.Form == XmlSchemaForm.Unqualified)
515 elem.Namespace = string.Empty;
516 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
517 elem.NestingLevel = att.NestingLevel;
520 elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
521 } else if (elem.TypeData.IsComplexType) {
522 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
525 if (att.ElementName.Length != 0) {
526 elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName);
527 } else if (elem.MappedType != null) {
528 elem.ElementName = elem.MappedType.ElementName;
530 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
538 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
540 elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
541 else if (elem.TypeData.IsComplexType)
542 elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
544 if (elem.MappedType != null) {
545 elem.ElementName = elem.MappedType.XmlType;
547 elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType;
550 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
551 elem.IsNullable = CanBeNull (elem.TypeData);
555 obmap.ItemInfo = list;
557 // If there can be different element names (types) in the array, then its name cannot
558 // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
561 if (list.Count > 1) {
562 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
564 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]);
565 if (elem.MappedType != null) {
566 baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
568 baseName = TypeTranslator.GetArrayName (elem.ElementName);
572 // Avoid name colisions
575 string name = baseName;
578 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
579 if (foundMap == null) nameCount = -1;
580 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
581 else name = baseName + (nameCount++);
583 while (nameCount != -1);
585 XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
586 map.ObjectMap = obmap;
588 // Register any of the including types as a derived class of object
589 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
591 XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
592 for (int i = 0; i < includes.Length; i++)
594 Type includedType = includes[i].Type;
595 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
598 // Register this map as a derived class of object
600 helper.RegisterSchemaType (map, name, defaultNamespace);
601 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
606 XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
608 Type type = typeData.Type;
609 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
610 if (map != null) return map;
612 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
613 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
615 if (type.BaseType != null)
617 XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace);
618 if (type.BaseType != typeof (object))
621 RegisterDerivedMap (bmap, map);
627 XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
629 Type type = typeData.Type;
630 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
631 if (map != null) return map;
632 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
633 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
637 // Enum.GetNames is not available in SL API
638 public static System.Collections.Generic.IEnumerable<string> GetEnumNames (Type type)
640 System.Collections.Generic.List<string> names = new System.Collections.Generic.List<string> ();
641 foreach (FieldInfo fi in type.GetFields (BindingFlags.Static | BindingFlags.Public))
646 XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
648 Type type = typeData.Type;
649 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
650 if (map != null) return map;
652 if (!allowPrivateTypes)
653 ReflectionHelper.CheckSerializableType (type, false);
655 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
656 map.IsNullable = false;
657 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
659 ArrayList members = new ArrayList();
661 foreach (string name in GetEnumNames (type)) {
663 string [] names = Enum.GetNames (type);
664 foreach (string name in names) {
666 FieldInfo field = type.GetField (name);
667 string xmlName = null;
668 if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
670 object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
671 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
672 if (xmlName == null) xmlName = name;
673 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
674 members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
677 bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
678 map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
679 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
683 XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
685 Type type = typeData.Type;
686 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
687 if (map != null) return map;
689 if (!allowPrivateTypes)
690 ReflectionHelper.CheckSerializableType (type, false);
692 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
693 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
697 void ImportIncludedTypes (Type type, string defaultNamespace)
699 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
700 for (int n=0; n<includes.Length; n++)
702 Type includedType = includes[n].Type;
703 ImportTypeMapping (includedType, null, defaultNamespace);
707 List<XmlReflectionMember> GetReflectionMembers (Type type)
709 // First we want to find the inheritance hierarchy in reverse order.
710 Type currentType = type;
711 ArrayList typeList = new ArrayList();
712 typeList.Add(currentType);
713 while (currentType != typeof(object))
715 currentType = currentType.BaseType; // Read the base type.
716 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
719 // Read all Fields via reflection.
720 ArrayList fieldList = new ArrayList();
721 FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
723 // This statement ensures fields are ordered starting from the base type.
724 for (int ti=0; ti<typeList.Count; ti++) {
725 for (int i=0; i<tfields.Length; i++) {
726 FieldInfo field = tfields[i];
727 if (field.DeclaringType == typeList[ti])
728 fieldList.Add (field);
733 int currentIndex = 0;
734 foreach (FieldInfo field in tfields)
736 // This statement ensures fields are ordered starting from the base type.
737 if (currentType != field.DeclaringType)
739 currentType = field.DeclaringType;
742 fieldList.Insert(currentIndex++, field);
745 // Read all Properties via reflection.
746 ArrayList propList = new ArrayList();
747 PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
749 // This statement ensures properties are ordered starting from the base type.
750 for (int ti=0; ti<typeList.Count; ti++) {
751 for (int i=0; i<tprops.Length; i++) {
752 PropertyInfo prop = tprops[i];
753 if (!prop.CanRead) continue;
754 if (prop.GetIndexParameters().Length > 0) continue;
755 if (prop.DeclaringType == typeList[ti])
762 foreach (PropertyInfo prop in tprops)
764 // This statement ensures properties are ordered starting from the base type.
765 if (currentType != prop.DeclaringType)
767 currentType = prop.DeclaringType;
770 if (!prop.CanRead) continue;
771 if (prop.GetIndexParameters().Length > 0) continue;
772 propList.Insert(currentIndex++, prop);
775 var members = new List<XmlReflectionMember>();
778 // We now step through the type hierarchy from the base (object) through
779 // to the supplied class, as each step outputting all Fields, and then
780 // all Properties. This is the exact same ordering as .NET 1.0/1.1.
781 foreach (Type t in typeList)
783 // Add any fields matching the current DeclaringType.
784 while (fieldIndex < fieldList.Count)
786 FieldInfo field = (FieldInfo)fieldList[fieldIndex];
787 if (field.DeclaringType==t)
790 XmlAttributes atts = attributeOverrides[type, field.Name];
791 if (atts == null) atts = new XmlAttributes (field);
792 if (atts.XmlIgnore) continue;
793 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
794 member.DeclaringType = field.DeclaringType;
800 // Add any properties matching the current DeclaringType.
801 while (propIndex < propList.Count)
803 PropertyInfo prop = (PropertyInfo)propList[propIndex];
804 if (prop.DeclaringType==t)
807 XmlAttributes atts = attributeOverrides[type, prop.Name];
808 if (atts == null) atts = new XmlAttributes (prop);
809 if (atts.XmlIgnore) continue;
810 if (!prop.CanWrite) {
811 if (prop.PropertyType.IsGenericType && TypeData.GetGenericListItemType (prop.PropertyType) == null) continue; // check this before calling GetTypeData() which raises error for missing Add(). See bug #704813.
812 if (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray) continue;
814 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
815 member.DeclaringType = prop.DeclaringType;
825 private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace)
827 XmlTypeMapMember mapMember;
828 XmlAttributes atts = rmember.XmlAttributes;
829 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
831 if (atts.XmlArray != null) {
832 if (atts.XmlArray.Namespace != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
833 throw new InvalidOperationException ("XmlArrayAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
834 if (typeData.SchemaType != SchemaTypes.Array &&
835 !(typeData.SchemaType == SchemaTypes.Primitive && typeData.Type == typeof (byte [])))
836 throw new InvalidOperationException ("XmlArrayAttribute can be applied to members of array or collection type.");
840 if (atts.XmlAnyAttribute != null)
842 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
843 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
845 mapMember = new XmlTypeMapMemberAnyAttribute();
848 throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
852 if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
854 // no XmlNode type check is done here (seealso: bug #553032).
855 XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
856 member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
861 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
862 mapMember = mapNamespaces;
864 else if (atts.XmlAttribute != null)
868 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
869 throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
871 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
872 if (atts.XmlAttribute.AttributeName.Length == 0)
873 mapAttribute.AttributeName = rmember.MemberName;
875 mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
877 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (mapAttribute.AttributeName);
879 if (typeData.IsComplexType)
880 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, defaultNamespace);
882 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
884 if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
885 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
886 mapAttribute.Form = XmlSchemaForm.Qualified;
887 mapAttribute.Namespace = atts.XmlAttribute.Namespace;
891 mapAttribute.Form = atts.XmlAttribute.Form;
892 if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
893 mapAttribute.Namespace = defaultNamespace;
895 mapAttribute.Namespace = "";
898 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
899 mapMember = mapAttribute;
901 else if (typeData.SchemaType == SchemaTypes.Array)
903 // If the member has a single XmlElementAttribute and the type is the type of the member,
904 // then it is not a flat list
906 if (atts.XmlElements.Count > 1 ||
907 (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
908 (atts.XmlText != null))
912 // check that it does not have XmlArrayAttribute
913 if (atts.XmlArray != null)
914 throw new InvalidOperationException ("XmlArrayAttribute cannot be used with members which also attributed with XmlElementAttribute or XmlTextAttribute.");
916 XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
917 member.ListMap = new ListMap ();
918 member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts);
919 member.ElementInfo = member.ListMap.ItemInfo;
920 member.ListMap.ChoiceMember = member.ChoiceMember;
927 XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
929 // Creates an ElementInfo that identifies the array instance.
930 member.ElementInfo = new XmlTypeMapElementInfoList();
931 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
932 elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName);
933 // note that it could be changed below (when Form is Unqualified)
934 elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
935 elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
936 elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
937 elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
938 elem.ExplicitOrder = (atts.XmlArray != null) ? atts.XmlArray.Order : -1;
939 // This is a bit tricky, but is done
940 // after filling descendant members, so
941 // that array items could be serialized
942 // with proper namespace.
943 if (atts.XmlArray != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
944 elem.Namespace = String.Empty;
946 member.ElementInfo.Add (elem);
954 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
955 member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts);
959 mapMember.DefaultValue = GetDefaultValue (typeData, atts.XmlDefaultValue);
960 mapMember.TypeData = typeData;
961 mapMember.Name = rmember.MemberName;
962 mapMember.IsReturnValue = rmember.IsReturnValue;
966 XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
968 EnumMap choiceEnumMap = null;
969 Type choiceEnumType = null;
971 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
972 ImportTextElementInfo (list, defaultType, member, atts, defaultNamespace);
974 if (atts.XmlChoiceIdentifier != null) {
976 throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context.");
978 member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName;
979 MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public);
981 if (mems.Length == 0)
982 throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls);
984 if (mems[0] is PropertyInfo) {
985 PropertyInfo pi = (PropertyInfo)mems[0];
986 if (!pi.CanWrite || !pi.CanRead)
987 throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write.");
988 choiceEnumType = pi.PropertyType;
990 else choiceEnumType = ((FieldInfo)mems[0]).FieldType;
992 member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType);
994 if (choiceEnumType.IsArray)
995 choiceEnumType = choiceEnumType.GetElementType ();
997 choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap;
998 if (choiceEnumMap == null)
999 throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute.");
1002 if (atts.XmlElements.Count == 0 && list.Count == 0)
1004 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
1005 elem.ElementName = defaultName;
1006 elem.Namespace = defaultNamespace;
1007 if (elem.TypeData.IsComplexType)
1008 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
1012 bool multiType = (atts.XmlElements.Count > 1);
1013 foreach (XmlElementAttribute att in atts.XmlElements)
1015 Type elemType = (att.Type != null) ? att.Type : defaultType;
1016 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
1017 elem.Form = att.Form;
1018 if (elem.Form != XmlSchemaForm.Unqualified)
1019 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
1020 elem.IsNullable = att.IsNullable;
1021 elem.ExplicitOrder = att.Order;
1023 if (elem.IsNullable && !elem.TypeData.IsNullable)
1024 throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
1026 if (elem.TypeData.IsComplexType)
1028 if (att.DataType.Length != 0) throw new InvalidOperationException (
1029 string.Format(CultureInfo.InvariantCulture, "'{0}' is "
1030 + "an invalid value for '{1}.{2}' of type '{3}'. "
1031 + "The property may only be specified for primitive types.",
1032 att.DataType, cls.FullName, defaultName,
1033 elem.TypeData.FullTypeName));
1034 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
1037 if (att.ElementName.Length != 0) {
1038 elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName);
1039 } else if (multiType) {
1040 if (elem.MappedType != null) {
1041 elem.ElementName = elem.MappedType.ElementName;
1043 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
1046 elem.ElementName = defaultName;
1049 if (choiceEnumMap != null) {
1050 string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
1052 throw new InvalidOperationException (string.Format (
1053 CultureInfo.InvariantCulture, "Type {0} is missing"
1054 + " enumeration value '{1}' for element '{1} from"
1055 + " namespace '{2}'.", choiceEnumType, elem.ElementName,
1057 elem.ChoiceValue = Enum.Parse (choiceEnumType, cname, false);
1065 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
1067 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
1069 ImportTextElementInfo (list, rmember.MemberType, member, atts, defaultNamespace);
1071 #if !MOONLIGHT // no practical anyElement support
1072 foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
1074 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
1075 if (att.Name.Length != 0)
1077 elem.ElementName = XmlConvert.EncodeLocalName(att.Name);
1078 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
1082 elem.IsUnnamedAnyElement = true;
1083 elem.Namespace = defaultNamespace;
1084 if (att.Namespace != null)
1085 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.");
1087 elem.ExplicitOrder = att.Order;
1094 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts, string defaultNamespace)
1096 if (atts.XmlText != null)
1098 member.IsXmlTextCollector = true;
1099 if (atts.XmlText.Type != null) {
1100 TypeData td = TypeTranslator.GetTypeData (defaultType);
1101 if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
1102 throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
1104 defaultType = atts.XmlText.Type;
1107 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes
1110 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
1112 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
1113 elem.TypeData.SchemaType != SchemaTypes.Enum &&
1114 elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
1115 !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
1117 throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
1119 if (elem.TypeData.IsComplexType)
1120 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
1121 elem.IsTextElement = true;
1122 elem.WrappedElement = false;
1127 bool CanBeNull (TypeData type)
1129 #if !NET_2_0 // idiotic compatibility
1130 if (type.Type == typeof (XmlQualifiedName))
1133 return !type.Type.IsValueType || type.IsNullable;
1136 public void IncludeType (Type type)
1139 throw new ArgumentNullException ("type");
1141 if (includedTypes == null) includedTypes = new ArrayList ();
1142 if (!includedTypes.Contains (type))
1143 includedTypes.Add (type);
1145 if (relatedMaps.Count > 0) {
1146 foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) {
1147 if (map.TypeData.Type == typeof(object))
1148 map.DerivedTypes.Add (ImportTypeMapping (type));
1153 public void IncludeTypes (ICustomAttributeProvider provider)
1155 object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
1157 foreach (XmlIncludeAttribute at in ats)
1158 IncludeType (at.Type);
1161 private object GetDefaultValue (TypeData typeData, object defaultValue)
1163 if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum)
1164 return defaultValue;
1167 string namedValue = (defaultValue as Enum).ToString ("g");
1168 string decimalValue = (defaultValue as Enum).ToString ("d");
1170 // get string representation of enum value
1171 string namedValue = Enum.Format (typeData.Type, defaultValue, "g");
1172 // get decimal representation of enum value
1173 string decimalValue = Enum.Format (typeData.Type, defaultValue, "d");
1175 // if decimal representation matches string representation, then
1176 // the value is not defined in the enum type (as the "g" format
1177 // will return the decimal equivalent of the value if the value
1178 // is not equal to a combination of named enumerated constants
1179 if (namedValue == decimalValue) {
1180 string msg = string.Format (CultureInfo.InvariantCulture,
1181 "Value '{0}' cannot be converted to {1}.", defaultValue,
1182 defaultValue.GetType ().FullName);
1183 throw new InvalidOperationException (msg);
1186 // XmlSerializer expects integral enum value
1187 //return namedValue.Replace (',', ' ');
1188 return defaultValue;
1191 #endregion // Methods