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
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Collections.Generic;
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 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, true);
116 XmlMembersMapping ImportMembersMapping (string elementName,
118 XmlReflectionMember[] members,
119 bool hasWrapperElement,
122 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, rpc, true);
129 XmlMembersMapping ImportMembersMapping (string elementName,
131 XmlReflectionMember[] members,
132 bool hasWrapperElement,
136 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, rpc, openModel, XmlMappingAccess.Read | XmlMappingAccess.Write);
140 [MonoTODO] // FIXME: handle writeAccessors, validate, and mapping access
143 XmlMembersMapping ImportMembersMapping (string elementName,
145 XmlReflectionMember[] members,
146 bool hasWrapperElement,
149 XmlMappingAccess access)
151 // Reset (); Disabled. See ChangeLog
153 ArrayList mapping = new ArrayList ();
154 for (int n=0; n<members.Length; n++)
156 XmlTypeMapMember mapMem = CreateMapMember (null, members[n], ns);
157 mapMem.GlobalIndex = n;
158 mapMem.CheckOptionalValueType (members);
159 mapping.Add (new XmlMemberMapping (members[n].MemberName, ns, mapMem, false));
161 elementName = XmlConvert.EncodeLocalName (elementName);
162 XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, (XmlMemberMapping[])mapping.ToArray (typeof(XmlMemberMapping)));
163 mps.RelatedMaps = relatedMaps;
164 mps.Format = SerializationFormat.Literal;
165 Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
167 mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
168 if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
173 public XmlTypeMapping ImportTypeMapping (Type type)
175 return ImportTypeMapping (type, null, null);
178 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
180 return ImportTypeMapping (type, null, defaultNamespace);
183 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root)
185 return ImportTypeMapping (type, root, null);
188 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
191 throw new ArgumentNullException ("type");
193 if (type == typeof (void))
194 throw new NotSupportedException ("The type " + type.FullName + " may not be serialized.");
196 return ImportTypeMapping (TypeTranslator.GetTypeData (type), root,
200 internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
202 return ImportTypeMapping (typeData, (XmlRootAttribute) null,
206 private XmlTypeMapping ImportTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
208 if (typeData == null)
209 throw new ArgumentNullException ("typeData");
211 if (typeData.Type == null)
212 throw new ArgumentException ("Specified TypeData instance does not have Type set.");
214 if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
215 if (defaultNamespace == null) defaultNamespace = string.Empty;
220 switch (typeData.SchemaType) {
221 case SchemaTypes.Class: map = ImportClassMapping (typeData, root, defaultNamespace); break;
222 case SchemaTypes.Array: map = ImportListMapping (typeData, root, defaultNamespace, null, 0); break;
223 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (typeData, root, defaultNamespace); break;
224 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (typeData, root, defaultNamespace); break;
225 case SchemaTypes.Enum: map = ImportEnumMapping (typeData, root, defaultNamespace); break;
226 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (typeData, root, defaultNamespace); break;
227 default: throw new NotSupportedException ("Type " + typeData.Type.FullName + " not supported for XML stialization");
232 map.SetKey (typeData.Type.ToString ());
234 map.RelatedMaps = relatedMaps;
235 map.Format = SerializationFormat.Literal;
236 Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
238 map.Source = new XmlTypeSerializationSource (typeData.Type, root, attributeOverrides, defaultNamespace, extraTypes);
239 if (allowPrivateTypes) map.Source.CanBeGenerated = false;
242 } catch (InvalidOperationException ex) {
243 throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
244 "There was an error reflecting type '{0}'.", typeData.Type.FullName), ex);
248 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
250 bool hasTypeNamespace = !string.IsNullOrEmpty (defaultNamespace);
251 string rootNamespace = null;
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;
279 hasTypeNamespace = true;
282 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
283 defaultXmlType = XmlConvert.EncodeLocalName (atts.XmlType.TypeName);
285 includeInSchema = atts.XmlType.IncludeInSchema;
288 elementName = defaultXmlType;
292 if (root.ElementName.Length != 0)
293 elementName = XmlConvert.EncodeLocalName(root.ElementName);
294 if (root.Namespace != null) {
295 rootNamespace = root.Namespace;
296 hasTypeNamespace = true;
298 nullable = root.IsNullable;
301 rootNamespace = rootNamespace ?? defaultNamespace ?? string.Empty;
302 typeNamespace = typeNamespace ?? rootNamespace;
305 switch (typeData.SchemaType) {
306 case SchemaTypes.XmlSerializable:
307 map = new XmlSerializableMapping (root, elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
309 case SchemaTypes.Primitive:
310 if (!typeData.IsXsdType)
311 map = new XmlTypeMapping (elementName, rootNamespace,
312 typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
314 map = new XmlTypeMapping (elementName, rootNamespace,
315 typeData, defaultXmlType, typeNamespace);
318 map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, hasTypeNamespace ? typeNamespace : null);
322 map.IncludeInSchema = includeInSchema;
323 map.IsNullable = nullable;
324 relatedMaps.Add (map);
329 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
331 TypeData typeData = TypeTranslator.GetTypeData (type);
332 return ImportClassMapping (typeData, root, defaultNamespace);
335 XmlTypeMapping ImportClassMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
337 Type type = typeData.Type;
339 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
340 if (map != null) return map;
342 if (!allowPrivateTypes)
343 ReflectionHelper.CheckSerializableType (type, false);
345 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
346 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
347 helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
351 ClassMap classMap = new ClassMap ();
352 map.ObjectMap = classMap;
354 var members = GetReflectionMembers (type);
355 bool? isOrderExplicit = null;
356 foreach (XmlReflectionMember rmember in members)
358 int? order = rmember.XmlAttributes.Order;
359 if (isOrderExplicit == null)
362 isOrderExplicit = (int) order >= 0;
364 else if (order != null && isOrderExplicit != ((int) order >= 0))
365 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.");
367 if (isOrderExplicit == true)
368 members.Sort ((m1, m2) => (int) m1.XmlAttributes.SortableOrder - (int) m2.XmlAttributes.SortableOrder);
370 foreach (XmlReflectionMember rmember in members)
372 string ns = map.XmlTypeNamespace;
373 if (rmember.XmlAttributes.XmlIgnore) continue;
374 if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
375 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
376 if (bmap.HasXmlTypeNamespace)
377 ns = bmap.XmlTypeNamespace;
381 XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
382 mem.CheckOptionalValueType (type);
383 classMap.AddMember (mem);
384 } catch (Exception ex) {
385 throw new InvalidOperationException (string.Format (
386 CultureInfo.InvariantCulture, "There was an error" +
387 " reflecting field '{0}'.", rmember.MemberName), ex);
391 // Import extra classes
393 if (type == typeof (object) && includedTypes != null)
395 foreach (Type intype in includedTypes)
396 map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
399 // Register inheritance relations
401 if (type.BaseType != null)
403 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
404 ClassMap cbmap = bmap.ObjectMap as ClassMap;
406 if (type.BaseType != typeof (object)) {
408 if (!cbmap.HasSimpleContent)
409 classMap.SetCanBeSimpleType (false);
412 // At this point, derived classes of this map must be already registered
414 RegisterDerivedMap (bmap, map);
416 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
417 throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
420 ImportIncludedTypes (type, defaultNamespace);
422 if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
424 XmlTypeMapMember mem = classMap.XmlTextCollector;
425 if (mem.TypeData.Type != typeof(string) &&
426 mem.TypeData.Type != typeof(string[]) &&
427 mem.TypeData.Type != typeof(XmlNode[]) &&
428 mem.TypeData.Type != typeof(object[]))
430 throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
436 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
438 map.DerivedTypes.Add (derivedMap);
439 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
441 if (map.BaseMap != null)
442 RegisterDerivedMap (map.BaseMap, derivedMap);
444 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
446 obmap.DerivedTypes.Add (derivedMap);
450 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
452 string typeNamespace = null;
454 XmlAttributes atts = null;
455 if (!typeData.IsListType)
457 if (attributeOverrides != null)
458 atts = attributeOverrides[typeData.Type];
462 atts = new XmlAttributes (typeData.Type);
464 if (atts.XmlType != null)
466 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
467 typeNamespace = atts.XmlType.Namespace;
470 if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
472 if (atts.XmlRoot != null && root == null)
477 if (root.Namespace != null && root.Namespace.Length != 0)
478 return root.Namespace;
481 if (defaultNamespace == null) return "";
482 else return defaultNamespace;
485 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
487 TypeData typeData = TypeTranslator.GetTypeData (type);
488 return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel);
491 XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
493 Type type = typeData.Type;
494 ListMap obmap = new ListMap ();
496 if (!allowPrivateTypes)
497 ReflectionHelper.CheckSerializableType (type, true);
499 if (atts == null) atts = new XmlAttributes();
500 Type itemType = typeData.ListItemType;
502 // warning: byte[][] should not be considered multiarray
503 bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
505 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
507 foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
509 if (att.Namespace != null && att.Form == XmlSchemaForm.Unqualified)
510 throw new InvalidOperationException ("XmlArrayItemAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
511 if (att.NestingLevel != nestingLevel) continue;
512 Type elemType = (att.Type != null) ? att.Type : itemType;
513 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
514 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
515 if (elem.Namespace == null) elem.Namespace = "";
516 elem.Form = att.Form;
517 if (att.Form == XmlSchemaForm.Unqualified)
518 elem.Namespace = string.Empty;
519 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
520 elem.NestingLevel = att.NestingLevel;
523 elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
524 } else if (elem.TypeData.IsComplexType) {
525 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
528 if (att.ElementName.Length != 0) {
529 elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName);
530 } else if (elem.MappedType != null) {
531 elem.ElementName = elem.MappedType.ElementName;
533 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
541 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
543 elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
544 else if (elem.TypeData.IsComplexType)
545 elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
547 if (elem.MappedType != null) {
548 elem.ElementName = elem.MappedType.XmlType;
550 elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType;
553 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
554 elem.IsNullable = CanBeNull (elem.TypeData);
558 obmap.ItemInfo = list;
560 // If there can be different element names (types) in the array, then its name cannot
561 // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
564 if (list.Count > 1) {
565 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
567 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]);
568 if (elem.MappedType != null) {
569 baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
571 baseName = TypeTranslator.GetArrayName (elem.ElementName);
575 // Avoid name colisions
578 string name = baseName;
581 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
582 if (foundMap == null) nameCount = -1;
583 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
584 else name = baseName + (nameCount++);
586 while (nameCount != -1);
588 XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
589 map.ObjectMap = obmap;
591 // Register any of the including types as a derived class of object
592 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
594 XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
595 for (int i = 0; i < includes.Length; i++)
597 Type includedType = includes[i].Type;
598 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
601 // Register this map as a derived class of object
603 helper.RegisterSchemaType (map, name, defaultNamespace);
604 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
609 XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
611 Type type = typeData.Type;
612 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
613 if (map != null) return map;
615 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
616 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
618 if (type.BaseType != null)
620 XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace);
621 if (type.BaseType != typeof (object))
624 RegisterDerivedMap (bmap, map);
630 XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
632 Type type = typeData.Type;
633 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
634 if (map != null) return map;
635 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
636 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
640 XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
642 Type type = typeData.Type;
643 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
644 if (map != null) return map;
646 if (!allowPrivateTypes)
647 ReflectionHelper.CheckSerializableType (type, false);
649 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
650 map.IsNullable = false;
651 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
653 ArrayList members = new ArrayList();
654 string [] names = Enum.GetNames (type);
655 foreach (string name in names) {
656 FieldInfo field = type.GetField (name);
657 string xmlName = null;
658 if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
660 object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
661 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
662 if (xmlName == null) xmlName = name;
663 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
664 members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
667 bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
668 map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
669 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
673 XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
675 Type type = typeData.Type;
676 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
677 if (map != null) return map;
679 if (!allowPrivateTypes)
680 ReflectionHelper.CheckSerializableType (type, false);
682 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
683 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
687 void ImportIncludedTypes (Type type, string defaultNamespace)
689 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
690 for (int n=0; n<includes.Length; n++)
692 Type includedType = includes[n].Type;
693 ImportTypeMapping (includedType, null, defaultNamespace);
697 List<XmlReflectionMember> GetReflectionMembers (Type type)
699 // First we want to find the inheritance hierarchy in reverse order.
700 Type currentType = type;
701 ArrayList typeList = new ArrayList();
702 typeList.Add(currentType);
703 while (currentType != typeof(object))
705 currentType = currentType.BaseType; // Read the base type.
706 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
709 // Read all Fields via reflection.
710 ArrayList fieldList = new ArrayList();
711 FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
713 // This statement ensures fields are ordered starting from the base type.
714 for (int ti=0; ti<typeList.Count; ti++) {
715 for (int i=0; i<tfields.Length; i++) {
716 FieldInfo field = tfields[i];
717 if (field.DeclaringType == typeList[ti])
718 fieldList.Add (field);
723 int currentIndex = 0;
724 foreach (FieldInfo field in tfields)
726 // This statement ensures fields are ordered starting from the base type.
727 if (currentType != field.DeclaringType)
729 currentType = field.DeclaringType;
732 fieldList.Insert(currentIndex++, field);
735 // Read all Properties via reflection.
736 ArrayList propList = new ArrayList();
737 PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
739 // This statement ensures properties are ordered starting from the base type.
740 for (int ti=0; ti<typeList.Count; ti++) {
741 for (int i=0; i<tprops.Length; i++) {
742 PropertyInfo prop = tprops[i];
743 if (!prop.CanRead) continue;
744 if (prop.GetIndexParameters().Length > 0) continue;
745 if (prop.DeclaringType == typeList[ti])
752 foreach (PropertyInfo prop in tprops)
754 // This statement ensures properties are ordered starting from the base type.
755 if (currentType != prop.DeclaringType)
757 currentType = prop.DeclaringType;
760 if (!prop.CanRead) continue;
761 if (prop.GetIndexParameters().Length > 0) continue;
762 propList.Insert(currentIndex++, prop);
765 var members = new List<XmlReflectionMember>();
768 // We now step through the type hierarchy from the base (object) through
769 // to the supplied class, as each step outputting all Fields, and then
770 // all Properties. This is the exact same ordering as .NET 1.0/1.1.
771 foreach (Type t in typeList)
773 // Add any fields matching the current DeclaringType.
774 while (fieldIndex < fieldList.Count)
776 FieldInfo field = (FieldInfo)fieldList[fieldIndex];
777 if (field.DeclaringType==t)
780 XmlAttributes atts = attributeOverrides[type, field.Name];
781 if (atts == null) atts = new XmlAttributes (field);
782 if (atts.XmlIgnore) continue;
783 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
784 member.DeclaringType = field.DeclaringType;
790 // Add any properties matching the current DeclaringType.
791 while (propIndex < propList.Count)
793 PropertyInfo prop = (PropertyInfo)propList[propIndex];
794 if (prop.DeclaringType==t)
797 XmlAttributes atts = attributeOverrides[type, prop.Name];
798 if (atts == null) atts = new XmlAttributes (prop);
799 if (atts.XmlIgnore) continue;
800 if (!prop.CanWrite) {
801 if (prop.PropertyType.IsInterface && typeof (IEnumerable).IsAssignableFrom (prop.PropertyType)) continue;
802 if (prop.PropertyType.IsGenericType && TypeData.GetGenericListItemType (prop.PropertyType) == null) continue; // check this before calling GetTypeData() which raises error for missing Add(). See bug #704813.
803 if (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray) continue;
805 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
806 member.DeclaringType = prop.DeclaringType;
816 private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace)
818 XmlTypeMapMember mapMember;
819 XmlAttributes atts = rmember.XmlAttributes;
820 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
822 if (atts.XmlArray != null) {
823 if (atts.XmlArray.Namespace != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
824 throw new InvalidOperationException ("XmlArrayAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
825 if (typeData.SchemaType != SchemaTypes.Array &&
826 !(typeData.SchemaType == SchemaTypes.Primitive && typeData.Type == typeof (byte [])))
827 throw new InvalidOperationException ("XmlArrayAttribute can be applied to members of array or collection type.");
830 if (atts.XmlAnyAttribute != null)
832 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
833 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
835 mapMember = new XmlTypeMapMemberAnyAttribute();
838 throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
841 if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
843 // no XmlNode type check is done here (seealso: bug #553032).
844 XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
845 member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
850 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
851 mapMember = mapNamespaces;
853 else if (atts.XmlAttribute != null)
857 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
858 throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
860 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
861 if (atts.XmlAttribute.AttributeName.Length == 0)
862 mapAttribute.AttributeName = rmember.MemberName;
864 mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
866 mapAttribute.AttributeName = XmlConvert.EncodeName (mapAttribute.AttributeName);
868 if (typeData.IsComplexType)
869 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, defaultNamespace);
871 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
873 if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
874 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
875 mapAttribute.Form = XmlSchemaForm.Qualified;
876 mapAttribute.Namespace = atts.XmlAttribute.Namespace;
880 mapAttribute.Form = atts.XmlAttribute.Form;
881 if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
882 mapAttribute.Namespace = defaultNamespace;
884 mapAttribute.Namespace = "";
887 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
888 mapMember = mapAttribute;
890 else if (typeData.SchemaType == SchemaTypes.Array)
892 // If the member has a single XmlElementAttribute and the type is the type of the member,
893 // then it is not a flat list
895 if (atts.XmlElements.Count > 1 ||
896 (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
897 (atts.XmlText != null))
901 // check that it does not have XmlArrayAttribute
902 if (atts.XmlArray != null)
903 throw new InvalidOperationException ("XmlArrayAttribute cannot be used with members which also attributed with XmlElementAttribute or XmlTextAttribute.");
905 XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
906 member.ListMap = new ListMap ();
907 member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts);
908 member.ElementInfo = member.ListMap.ItemInfo;
909 member.ListMap.ChoiceMember = member.ChoiceMember;
916 XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
918 // Creates an ElementInfo that identifies the array instance.
919 member.ElementInfo = new XmlTypeMapElementInfoList();
920 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
921 elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName);
922 // note that it could be changed below (when Form is Unqualified)
923 elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
924 elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
925 elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
926 elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
927 elem.ExplicitOrder = (atts.XmlArray != null) ? atts.XmlArray.Order : -1;
928 // This is a bit tricky, but is done
929 // after filling descendant members, so
930 // that array items could be serialized
931 // with proper namespace.
932 if (atts.XmlArray != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
933 elem.Namespace = String.Empty;
935 member.ElementInfo.Add (elem);
943 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
944 member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts);
948 mapMember.DefaultValue = GetDefaultValue (typeData, atts.XmlDefaultValue);
949 mapMember.TypeData = typeData;
950 mapMember.Name = rmember.MemberName;
951 mapMember.IsReturnValue = rmember.IsReturnValue;
955 XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
957 EnumMap choiceEnumMap = null;
958 Type choiceEnumType = null;
960 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
961 ImportTextElementInfo (list, defaultType, member, atts, defaultNamespace);
963 if (atts.XmlChoiceIdentifier != null) {
965 throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context.");
967 member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName;
968 MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public);
970 if (mems.Length == 0)
971 throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls);
973 if (mems[0] is PropertyInfo) {
974 PropertyInfo pi = (PropertyInfo)mems[0];
975 if (!pi.CanWrite || !pi.CanRead)
976 throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write.");
977 choiceEnumType = pi.PropertyType;
979 else choiceEnumType = ((FieldInfo)mems[0]).FieldType;
981 member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType);
983 if (choiceEnumType.IsArray)
984 choiceEnumType = choiceEnumType.GetElementType ();
986 choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap;
987 if (choiceEnumMap == null)
988 throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute.");
991 if (atts.XmlElements.Count == 0 && list.Count == 0)
993 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
994 elem.ElementName = defaultName;
995 elem.Namespace = defaultNamespace;
996 if (elem.TypeData.IsComplexType)
997 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
1001 bool multiType = (atts.XmlElements.Count > 1);
1002 foreach (XmlElementAttribute att in atts.XmlElements)
1004 Type elemType = (att.Type != null) ? att.Type : defaultType;
1005 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
1006 elem.Form = att.Form;
1007 if (elem.Form != XmlSchemaForm.Unqualified)
1008 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
1010 // elem may already be nullable, and IsNullable property in XmlElement is false by default
1011 if (att.IsNullable && !elem.IsNullable)
1012 elem.IsNullable = att.IsNullable;
1014 elem.ExplicitOrder = att.Order;
1016 if (elem.IsNullable && !elem.TypeData.IsNullable)
1017 throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
1019 if (elem.TypeData.IsComplexType)
1021 if (att.DataType.Length != 0) throw new InvalidOperationException (
1022 string.Format(CultureInfo.InvariantCulture, "'{0}' is "
1023 + "an invalid value for '{1}.{2}' of type '{3}'. "
1024 + "The property may only be specified for primitive types.",
1025 att.DataType, cls.FullName, defaultName,
1026 elem.TypeData.FullTypeName));
1027 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
1030 if (att.ElementName.Length != 0) {
1031 elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName);
1032 } else if (multiType) {
1033 if (elem.MappedType != null) {
1034 elem.ElementName = elem.MappedType.ElementName;
1036 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
1039 elem.ElementName = defaultName;
1042 if (choiceEnumMap != null) {
1043 string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
1045 throw new InvalidOperationException (string.Format (
1046 CultureInfo.InvariantCulture, "Type {0} is missing"
1047 + " enumeration value '{1}' for element '{1} from"
1048 + " namespace '{2}'.", choiceEnumType, elem.ElementName,
1050 elem.ChoiceValue = Enum.Parse (choiceEnumType, cname, false);
1058 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
1060 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
1062 ImportTextElementInfo (list, rmember.MemberType, member, atts, defaultNamespace);
1064 foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
1066 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
1067 if (att.Name.Length != 0)
1069 elem.ElementName = XmlConvert.EncodeLocalName(att.Name);
1070 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
1074 elem.IsUnnamedAnyElement = true;
1075 elem.Namespace = defaultNamespace;
1076 if (att.Namespace != null)
1077 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.");
1079 elem.ExplicitOrder = att.Order;
1085 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts, string defaultNamespace)
1087 if (atts.XmlText != null)
1089 member.IsXmlTextCollector = true;
1090 if (atts.XmlText.Type != null) {
1091 TypeData td = TypeTranslator.GetTypeData (defaultType);
1092 if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
1093 throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
1095 defaultType = atts.XmlText.Type;
1097 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes
1099 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
1101 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
1102 elem.TypeData.SchemaType != SchemaTypes.Enum &&
1103 elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
1104 !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
1106 throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
1108 if (elem.TypeData.IsComplexType)
1109 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
1110 elem.IsTextElement = true;
1111 elem.WrappedElement = false;
1116 bool CanBeNull (TypeData type)
1118 #if !NET_2_0 // idiotic compatibility
1119 if (type.Type == typeof (XmlQualifiedName))
1122 return !type.Type.IsValueType || type.IsNullable;
1125 public void IncludeType (Type type)
1128 throw new ArgumentNullException ("type");
1130 if (includedTypes == null) includedTypes = new ArrayList ();
1131 if (!includedTypes.Contains (type))
1132 includedTypes.Add (type);
1134 if (relatedMaps.Count > 0) {
1135 foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) {
1136 if (map.TypeData.Type == typeof(object))
1137 map.DerivedTypes.Add (ImportTypeMapping (type));
1142 public void IncludeTypes (ICustomAttributeProvider provider)
1144 object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
1146 foreach (XmlIncludeAttribute at in ats)
1147 IncludeType (at.Type);
1150 private object GetDefaultValue (TypeData typeData, object defaultValue)
1152 if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum)
1153 return defaultValue;
1155 // get string representation of enum value
1156 string namedValue = Enum.Format (typeData.Type, defaultValue, "g");
1157 // get decimal representation of enum value
1158 string decimalValue = Enum.Format (typeData.Type, defaultValue, "d");
1159 // if decimal representation matches string representation, then
1160 // the value is not defined in the enum type (as the "g" format
1161 // will return the decimal equivalent of the value if the value
1162 // is not equal to a combination of named enumerated constants
1163 if (namedValue == decimalValue) {
1164 string msg = string.Format (CultureInfo.InvariantCulture,
1165 "Value '{0}' cannot be converted to {1}.", defaultValue,
1166 defaultValue.GetType ().FullName);
1167 throw new InvalidOperationException (msg);
1170 // XmlSerializer expects integral enum value
1171 //return namedValue.Replace (',', ' ');
1172 return defaultValue;
1175 #endregion // Methods