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 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, writeAccessors, true);
129 XmlMembersMapping ImportMembersMapping (string elementName,
131 XmlReflectionMember[] members,
132 bool hasWrapperElement,
136 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, validate, 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 XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
154 for (int n=0; n<members.Length; n++)
156 XmlTypeMapMember mapMem = CreateMapMember (null, members[n], ns);
157 mapping[n] = new XmlMemberMapping (members[n].MemberName, ns, mapMem, false);
159 elementName = XmlConvert.EncodeLocalName (elementName);
160 XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, mapping);
161 mps.RelatedMaps = relatedMaps;
162 mps.Format = SerializationFormat.Literal;
163 Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
164 mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
165 if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
169 public XmlTypeMapping ImportTypeMapping (Type type)
171 return ImportTypeMapping (type, null, null);
174 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
176 return ImportTypeMapping (type, null, defaultNamespace);
179 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute group)
181 return ImportTypeMapping (type, group, null);
184 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
187 throw new ArgumentNullException ("type");
189 if (type == typeof (void))
190 throw new NotSupportedException ("The type " + type.FullName + " may not be serialized.");
192 return ImportTypeMapping (TypeTranslator.GetTypeData (type), root,
196 internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
198 return ImportTypeMapping (typeData, (XmlRootAttribute) null,
202 private XmlTypeMapping ImportTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
204 if (typeData == null)
205 throw new ArgumentNullException ("typeData");
207 if (typeData.Type == null)
208 throw new ArgumentException ("Specified TypeData instance does not have Type set.");
210 if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
211 if (defaultNamespace == null) defaultNamespace = string.Empty;
216 switch (typeData.SchemaType) {
217 case SchemaTypes.Class: map = ImportClassMapping (typeData, root, defaultNamespace); break;
218 case SchemaTypes.Array: map = ImportListMapping (typeData, root, defaultNamespace, null, 0); break;
219 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (typeData, root, defaultNamespace); break;
220 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (typeData, root, defaultNamespace); break;
221 case SchemaTypes.Enum: map = ImportEnumMapping (typeData, root, defaultNamespace); break;
222 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (typeData, root, defaultNamespace); break;
223 default: throw new NotSupportedException ("Type " + typeData.Type.FullName + " not supported for XML stialization");
226 map.RelatedMaps = relatedMaps;
227 map.Format = SerializationFormat.Literal;
228 Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
229 map.Source = new XmlTypeSerializationSource (typeData.Type, root, attributeOverrides, defaultNamespace, extraTypes);
230 if (allowPrivateTypes) map.Source.CanBeGenerated = false;
232 } catch (InvalidOperationException ex) {
233 throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
234 "There was an error reflecting type '{0}'.", typeData.Type.FullName), ex);
238 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
240 string rootNamespace = defaultNamespace;
241 string typeNamespace = null;
243 bool includeInSchema = true;
244 XmlAttributes atts = null;
245 bool nullable = CanBeNull (typeData);
247 if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
249 if (!typeData.IsListType)
251 if (attributeOverrides != null)
252 atts = attributeOverrides[typeData.Type];
254 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
255 throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
259 atts = new XmlAttributes (typeData.Type);
261 if (atts.XmlRoot != null && root == null)
264 if (atts.XmlType != null)
266 if (atts.XmlType.Namespace != null && typeData.SchemaType != SchemaTypes.Enum)
267 typeNamespace = atts.XmlType.Namespace;
269 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
270 defaultXmlType = XmlConvert.EncodeLocalName (atts.XmlType.TypeName);
272 includeInSchema = atts.XmlType.IncludeInSchema;
275 elementName = defaultXmlType;
279 if (root.ElementName.Length != 0)
280 elementName = XmlConvert.EncodeLocalName(root.ElementName);
281 if (root.Namespace != null)
282 rootNamespace = root.Namespace;
283 nullable = root.IsNullable;
286 if (rootNamespace == null) rootNamespace = "";
287 if (typeNamespace == null) typeNamespace = rootNamespace;
290 switch (typeData.SchemaType) {
291 case SchemaTypes.XmlSerializable:
292 map = new XmlSerializableMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
294 case SchemaTypes.Primitive:
295 if (!typeData.IsXsdType)
296 map = new XmlTypeMapping (elementName, rootNamespace,
297 typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
299 map = new XmlTypeMapping (elementName, rootNamespace,
300 typeData, defaultXmlType, typeNamespace);
303 map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
307 map.IncludeInSchema = includeInSchema;
308 map.IsNullable = nullable;
309 relatedMaps.Add (map);
314 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
316 TypeData typeData = TypeTranslator.GetTypeData (type);
317 return ImportClassMapping (typeData, root, defaultNamespace);
320 XmlTypeMapping ImportClassMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
322 Type type = typeData.Type;
324 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
325 if (map != null) return map;
327 if (!allowPrivateTypes)
328 ReflectionHelper.CheckSerializableType (type, false);
330 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
331 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
332 helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
336 ClassMap classMap = new ClassMap ();
337 map.ObjectMap = classMap;
339 ICollection members = GetReflectionMembers (type);
340 foreach (XmlReflectionMember rmember in members)
342 string ns = map.XmlTypeNamespace;
343 if (rmember.XmlAttributes.XmlIgnore) continue;
344 if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
345 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
346 ns = bmap.XmlTypeNamespace;
350 XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
351 mem.CheckOptionalValueType (type);
352 classMap.AddMember (mem);
353 } catch (Exception ex) {
354 throw new InvalidOperationException (string.Format (
355 CultureInfo.InvariantCulture, "There was an error" +
356 " reflecting field '{0}'.", rmember.MemberName), ex);
360 // Import extra classes
362 if (type == typeof (object) && includedTypes != null)
364 foreach (Type intype in includedTypes)
365 map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
368 // Register inheritance relations
370 if (type.BaseType != null)
372 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
373 ClassMap cbmap = bmap.ObjectMap as ClassMap;
375 if (type.BaseType != typeof (object)) {
377 if (!cbmap.HasSimpleContent)
378 classMap.SetCanBeSimpleType (false);
381 // At this point, derived classes of this map must be already registered
383 RegisterDerivedMap (bmap, map);
385 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
386 throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
389 ImportIncludedTypes (type, defaultNamespace);
391 if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
393 XmlTypeMapMember mem = classMap.XmlTextCollector;
394 if (mem.TypeData.Type != typeof(string) &&
395 mem.TypeData.Type != typeof(string[]) &&
396 mem.TypeData.Type != typeof(object[]) &&
397 mem.TypeData.Type != typeof(XmlNode[]))
399 throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
405 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
407 map.DerivedTypes.Add (derivedMap);
408 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
410 if (map.BaseMap != null)
411 RegisterDerivedMap (map.BaseMap, derivedMap);
413 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
415 obmap.DerivedTypes.Add (derivedMap);
419 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
421 string typeNamespace = null;
423 XmlAttributes atts = null;
424 if (!typeData.IsListType)
426 if (attributeOverrides != null)
427 atts = attributeOverrides[typeData.Type];
431 atts = new XmlAttributes (typeData.Type);
433 if (atts.XmlType != null)
435 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
436 typeNamespace = atts.XmlType.Namespace;
439 if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
441 if (atts.XmlRoot != null && root == null)
446 if (root.Namespace != null && root.Namespace.Length != 0)
447 return root.Namespace;
450 if (defaultNamespace == null) return "";
451 else return defaultNamespace;
454 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
456 TypeData typeData = TypeTranslator.GetTypeData (type);
457 return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel);
460 XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
462 Type type = typeData.Type;
463 ListMap obmap = new ListMap ();
465 if (!allowPrivateTypes)
466 ReflectionHelper.CheckSerializableType (type, true);
468 if (atts == null) atts = new XmlAttributes();
469 Type itemType = typeData.ListItemType;
471 // warning: byte[][] should not be considered multiarray
472 bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
474 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
476 foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
478 if (att.NestingLevel != nestingLevel) continue;
479 Type elemType = (att.Type != null) ? att.Type : itemType;
480 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
481 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
482 if (elem.Namespace == null) elem.Namespace = "";
483 elem.Form = att.Form;
484 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
485 elem.NestingLevel = att.NestingLevel;
488 elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
489 } else if (elem.TypeData.IsComplexType) {
490 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
493 if (att.ElementName.Length != 0) {
494 elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName);
495 } else if (elem.MappedType != null) {
496 elem.ElementName = elem.MappedType.ElementName;
498 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
506 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
508 elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
509 else if (elem.TypeData.IsComplexType)
510 elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
512 if (elem.MappedType != null) {
513 elem.ElementName = elem.MappedType.XmlType;
515 elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType;
518 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
519 elem.IsNullable = CanBeNull (elem.TypeData);
523 obmap.ItemInfo = list;
525 // If there can be different element names (types) in the array, then its name cannot
526 // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
529 if (list.Count > 1) {
530 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
532 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]);
533 if (elem.MappedType != null) {
534 baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
536 baseName = TypeTranslator.GetArrayName (elem.ElementName);
540 // Avoid name colisions
543 string name = baseName;
546 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
547 if (foundMap == null) nameCount = -1;
548 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
549 else name = baseName + (nameCount++);
551 while (nameCount != -1);
553 XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
554 map.ObjectMap = obmap;
556 // Register any of the including types as a derived class of object
557 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
559 XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
560 for (int i = 0; i < includes.Length; i++)
562 Type includedType = includes[i].Type;
563 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
566 // Register this map as a derived class of object
568 helper.RegisterSchemaType (map, name, defaultNamespace);
569 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
574 XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
576 Type type = typeData.Type;
577 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
578 if (map != null) return map;
580 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
581 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
583 if (type.BaseType != null)
585 XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace);
586 if (type.BaseType != typeof (object))
589 RegisterDerivedMap (bmap, map);
595 XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
597 Type type = typeData.Type;
598 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
599 if (map != null) return map;
600 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
601 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
605 XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
607 Type type = typeData.Type;
608 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
609 if (map != null) return map;
611 if (!allowPrivateTypes)
612 ReflectionHelper.CheckSerializableType (type, false);
614 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
615 map.IsNullable = false;
616 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
618 string [] names = Enum.GetNames (type);
619 ArrayList members = new ArrayList();
620 foreach (string name in names)
622 FieldInfo field = type.GetField (name);
623 string xmlName = null;
624 if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
626 object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
627 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
628 if (xmlName == null) xmlName = name;
629 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
630 members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
633 bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
634 map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
635 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
639 XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
641 Type type = typeData.Type;
642 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
643 if (map != null) return map;
645 if (!allowPrivateTypes)
646 ReflectionHelper.CheckSerializableType (type, false);
648 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
649 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
653 void ImportIncludedTypes (Type type, string defaultNamespace)
655 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
656 for (int n=0; n<includes.Length; n++)
658 Type includedType = includes[n].Type;
659 ImportTypeMapping (includedType, null, defaultNamespace);
663 ICollection GetReflectionMembers (Type type)
665 // First we want to find the inheritance hierarchy in reverse order.
666 Type currentType = type;
667 ArrayList typeList = new ArrayList();
668 typeList.Add(currentType);
669 while (currentType != typeof(object))
671 currentType = currentType.BaseType; // Read the base type.
672 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
675 // Read all Fields via reflection.
676 ArrayList fieldList = new ArrayList();
677 FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
679 // This statement ensures fields are ordered starting from the base type.
680 for (int ti=0; ti<typeList.Count; ti++) {
681 for (int i=0; i<tfields.Length; i++) {
682 FieldInfo field = tfields[i];
683 if (field.DeclaringType == typeList[ti])
684 fieldList.Add (field);
689 int currentIndex = 0;
690 foreach (FieldInfo field in tfields)
692 // This statement ensures fields are ordered starting from the base type.
693 if (currentType != field.DeclaringType)
695 currentType = field.DeclaringType;
698 fieldList.Insert(currentIndex++, field);
701 // Read all Properties via reflection.
702 ArrayList propList = new ArrayList();
703 PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
705 // This statement ensures properties are ordered starting from the base type.
706 for (int ti=0; ti<typeList.Count; ti++) {
707 for (int i=0; i<tprops.Length; i++) {
708 PropertyInfo prop = tprops[i];
709 if (!prop.CanRead) continue;
710 if (prop.GetIndexParameters().Length > 0) continue;
711 if (prop.DeclaringType == typeList[ti])
718 foreach (PropertyInfo prop in tprops)
720 // This statement ensures properties are ordered starting from the base type.
721 if (currentType != prop.DeclaringType)
723 currentType = prop.DeclaringType;
726 if (!prop.CanRead) continue;
727 if (prop.GetIndexParameters().Length > 0) continue;
728 propList.Insert(currentIndex++, prop);
731 ArrayList members = new ArrayList();
734 // We now step through the type hierarchy from the base (object) through
735 // to the supplied class, as each step outputting all Fields, and then
736 // all Properties. This is the exact same ordering as .NET 1.0/1.1.
737 foreach (Type t in typeList)
739 // Add any fields matching the current DeclaringType.
740 while (fieldIndex < fieldList.Count)
742 FieldInfo field = (FieldInfo)fieldList[fieldIndex];
743 if (field.DeclaringType==t)
746 XmlAttributes atts = attributeOverrides[type, field.Name];
747 if (atts == null) atts = new XmlAttributes (field);
748 if (atts.XmlIgnore) continue;
749 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
750 member.DeclaringType = field.DeclaringType;
756 // Add any properties matching the current DeclaringType.
757 while (propIndex < propList.Count)
759 PropertyInfo prop = (PropertyInfo)propList[propIndex];
760 if (prop.DeclaringType==t)
763 XmlAttributes atts = attributeOverrides[type, prop.Name];
764 if (atts == null) atts = new XmlAttributes (prop);
765 if (atts.XmlIgnore) continue;
766 if (!prop.CanWrite && (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray)) continue;
767 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
768 member.DeclaringType = prop.DeclaringType;
777 private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace)
779 XmlTypeMapMember mapMember;
780 XmlAttributes atts = rmember.XmlAttributes;
781 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
783 if (atts.XmlAnyAttribute != null)
785 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
786 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
788 mapMember = new XmlTypeMapMemberAnyAttribute();
791 throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
793 else if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
795 if ( (rmember.MemberType.FullName == "System.Xml.XmlElement[]") ||
796 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") ||
797 (rmember.MemberType.FullName == "System.Xml.XmlElement"))
799 XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
800 member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
804 throw new InvalidOperationException ("XmlAnyElementAttribute can only be applied to members of type XmlElement, XmlElement[] or XmlNode[]");
808 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
809 mapMember = mapNamespaces;
811 else if (atts.XmlAttribute != null)
815 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
816 throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
818 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
819 if (atts.XmlAttribute.AttributeName.Length == 0)
820 mapAttribute.AttributeName = rmember.MemberName;
822 mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
824 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (mapAttribute.AttributeName);
826 if (typeData.IsComplexType)
827 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, mapAttribute.Namespace);
829 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
831 if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
832 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
833 mapAttribute.Form = XmlSchemaForm.Qualified;
834 mapAttribute.Namespace = atts.XmlAttribute.Namespace;
838 mapAttribute.Form = atts.XmlAttribute.Form;
839 if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
840 mapAttribute.Namespace = defaultNamespace;
842 mapAttribute.Namespace = "";
845 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
846 mapMember = mapAttribute;
848 else if (typeData.SchemaType == SchemaTypes.Array)
850 // If the member has a single XmlElementAttribute and the type is the type of the member,
851 // then it is not a flat list
853 if (atts.XmlElements.Count > 1 ||
854 (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
855 (atts.XmlText != null))
859 // TODO: check that it does not have XmlArrayAttribute
860 XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
861 member.ListMap = new ListMap ();
862 member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts);
863 member.ElementInfo = member.ListMap.ItemInfo;
864 member.ListMap.ChoiceMember = member.ChoiceMember;
871 XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
873 // Creates an ElementInfo that identifies the array instance.
874 member.ElementInfo = new XmlTypeMapElementInfoList();
875 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
876 elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName);
877 elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
878 elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
879 elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
880 elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
882 member.ElementInfo.Add (elem);
890 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
891 member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts);
895 mapMember.DefaultValue = GetDefaultValue (typeData, atts.XmlDefaultValue);
896 mapMember.TypeData = typeData;
897 mapMember.Name = rmember.MemberName;
898 mapMember.IsReturnValue = rmember.IsReturnValue;
902 XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
904 EnumMap choiceEnumMap = null;
905 Type choiceEnumType = null;
907 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
908 ImportTextElementInfo (list, defaultType, member, atts);
910 if (atts.XmlChoiceIdentifier != null) {
912 throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context.");
914 member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName;
915 MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public);
917 if (mems.Length == 0)
918 throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls);
920 if (mems[0] is PropertyInfo) {
921 PropertyInfo pi = (PropertyInfo)mems[0];
922 if (!pi.CanWrite || !pi.CanRead)
923 throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write.");
924 choiceEnumType = pi.PropertyType;
926 else choiceEnumType = ((FieldInfo)mems[0]).FieldType;
928 member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType);
930 if (choiceEnumType.IsArray)
931 choiceEnumType = choiceEnumType.GetElementType ();
933 choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap;
934 if (choiceEnumMap == null)
935 throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute.");
938 if (atts.XmlElements.Count == 0 && list.Count == 0)
940 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
941 elem.ElementName = defaultName;
942 elem.Namespace = defaultNamespace;
943 if (elem.TypeData.IsComplexType)
944 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
948 bool multiType = (atts.XmlElements.Count > 1);
949 foreach (XmlElementAttribute att in atts.XmlElements)
951 Type elemType = (att.Type != null) ? att.Type : defaultType;
952 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
953 elem.Form = att.Form;
954 if (elem.Form != XmlSchemaForm.Unqualified)
955 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
956 elem.IsNullable = att.IsNullable;
958 if (elem.IsNullable && !elem.TypeData.IsNullable)
959 throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
961 if (elem.TypeData.IsComplexType)
963 if (att.DataType.Length != 0) throw new InvalidOperationException (
964 string.Format(CultureInfo.InvariantCulture, "'{0}' is "
965 + "an invalid value for '{1}.{2}' of type '{3}'. "
966 + "The property may only be specified for primitive types.",
967 att.DataType, cls.FullName, defaultName,
968 elem.TypeData.FullTypeName));
969 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
972 if (att.ElementName.Length != 0) {
973 elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName);
974 } else if (multiType) {
975 if (elem.MappedType != null) {
976 elem.ElementName = elem.MappedType.ElementName;
978 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
981 elem.ElementName = defaultName;
984 if (choiceEnumMap != null) {
985 string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
987 throw new InvalidOperationException (string.Format (
988 CultureInfo.InvariantCulture, "Type {0} is missing"
989 + " enumeration value '{1}' for element '{1} from"
990 + " namespace '{2}'.", choiceEnumType, elem.ElementName,
992 elem.ChoiceValue = Enum.Parse (choiceEnumType, cname);
1000 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
1002 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
1004 ImportTextElementInfo (list, rmember.MemberType, member, atts);
1006 foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
1008 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
1009 if (att.Name.Length != 0)
1011 elem.ElementName = XmlConvert.EncodeLocalName(att.Name);
1012 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
1016 elem.IsUnnamedAnyElement = true;
1017 elem.Namespace = defaultNamespace;
1018 if (att.Namespace != null)
1019 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.");
1026 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
1028 if (atts.XmlText != null)
1030 member.IsXmlTextCollector = true;
1031 if (atts.XmlText.Type != null) {
1032 TypeData td = TypeTranslator.GetTypeData (defaultType);
1033 if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
1034 throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
1036 defaultType = atts.XmlText.Type;
1038 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes
1040 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
1042 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
1043 elem.TypeData.SchemaType != SchemaTypes.Enum &&
1044 elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
1045 !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
1047 throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
1049 elem.IsTextElement = true;
1050 elem.WrappedElement = false;
1055 bool CanBeNull (TypeData type)
1057 #if !NET_2_0 // idiotic compatibility
1058 if (type.Type == typeof (XmlQualifiedName))
1061 return !type.Type.IsValueType;
1064 public void IncludeType (Type type)
1067 throw new ArgumentNullException ("type");
1069 if (includedTypes == null) includedTypes = new ArrayList ();
1070 if (!includedTypes.Contains (type))
1071 includedTypes.Add (type);
1073 if (relatedMaps.Count > 0) {
1074 foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) {
1075 if (map.TypeData.Type == typeof(object))
1076 map.DerivedTypes.Add (ImportTypeMapping (type));
1081 public void IncludeTypes (ICustomAttributeProvider provider)
1083 object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
1085 foreach (XmlIncludeAttribute at in ats)
1086 IncludeType (at.Type);
1089 private object GetDefaultValue (TypeData typeData, object defaultValue)
1091 if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum)
1092 return defaultValue;
1094 // get string representation of enum value
1095 string namedValue = Enum.Format (typeData.Type, defaultValue, "g");
1096 // get decimal representation of enum value
1097 string decimalValue = Enum.Format (typeData.Type, defaultValue, "d");
1099 // if decimal representation matches string representation, then
1100 // the value is not defined in the enum type (as the "g" format
1101 // will return the decimal equivalent of the value if the value
1102 // is not equal to a combination of named enumerated constants
1103 if (namedValue == decimalValue) {
1104 string msg = string.Format (CultureInfo.InvariantCulture,
1105 "Value '{0}' cannot be converted to {1}.", defaultValue,
1106 defaultValue.GetType ().FullName);
1107 throw new InvalidOperationException (msg);
1110 // XmlSerializer expects integral enum value
1111 //return namedValue.Replace (',', ' ');
1112 return defaultValue;
1115 #endregion // Methods