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, writeAccessors, true);
130 XmlMembersMapping ImportMembersMapping (string elementName,
132 XmlReflectionMember[] members,
133 bool hasWrapperElement,
137 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, validate, 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 if (members[n].XmlAttributes.XmlIgnore) continue;
158 XmlTypeMapMember mapMem = CreateMapMember (null, members[n], ns);
159 mapMem.GlobalIndex = n;
160 mapMem.CheckOptionalValueType (members);
161 mapping.Add (new XmlMemberMapping (members[n].MemberName, ns, mapMem, false));
163 elementName = XmlConvert.EncodeLocalName (elementName);
164 XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, (XmlMemberMapping[])mapping.ToArray (typeof(XmlMemberMapping)));
165 mps.RelatedMaps = relatedMaps;
166 mps.Format = SerializationFormat.Literal;
167 Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
169 mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
170 if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
175 public XmlTypeMapping ImportTypeMapping (Type type)
177 return ImportTypeMapping (type, null, null);
180 public XmlTypeMapping ImportTypeMapping (Type type, string defaultNamespace)
182 return ImportTypeMapping (type, null, defaultNamespace);
185 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute group)
187 return ImportTypeMapping (type, group, null);
190 public XmlTypeMapping ImportTypeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
193 throw new ArgumentNullException ("type");
195 if (type == typeof (void))
196 throw new NotSupportedException ("The type " + type.FullName + " may not be serialized.");
198 return ImportTypeMapping (TypeTranslator.GetTypeData (type), root,
202 internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
204 return ImportTypeMapping (typeData, (XmlRootAttribute) null,
208 private XmlTypeMapping ImportTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
210 if (typeData == null)
211 throw new ArgumentNullException ("typeData");
213 if (typeData.Type == null)
214 throw new ArgumentException ("Specified TypeData instance does not have Type set.");
216 if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
217 if (defaultNamespace == null) defaultNamespace = string.Empty;
222 switch (typeData.SchemaType) {
223 case SchemaTypes.Class: map = ImportClassMapping (typeData, root, defaultNamespace); break;
224 case SchemaTypes.Array: map = ImportListMapping (typeData, root, defaultNamespace, null, 0); break;
225 case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (typeData, root, defaultNamespace); break;
226 case SchemaTypes.Primitive: map = ImportPrimitiveMapping (typeData, root, defaultNamespace); break;
227 case SchemaTypes.Enum: map = ImportEnumMapping (typeData, root, defaultNamespace); break;
228 case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (typeData, root, defaultNamespace); break;
229 default: throw new NotSupportedException ("Type " + typeData.Type.FullName + " not supported for XML stialization");
234 map.SetKey (typeData.Type.ToString ());
236 map.RelatedMaps = relatedMaps;
237 map.Format = SerializationFormat.Literal;
238 Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
240 map.Source = new XmlTypeSerializationSource (typeData.Type, root, attributeOverrides, defaultNamespace, extraTypes);
241 if (allowPrivateTypes) map.Source.CanBeGenerated = false;
244 } catch (InvalidOperationException ex) {
245 throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
246 "There was an error reflecting type '{0}'.", typeData.Type.FullName), ex);
250 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
252 string rootNamespace = defaultNamespace;
253 string typeNamespace = null;
255 bool includeInSchema = true;
256 XmlAttributes atts = null;
257 bool nullable = CanBeNull (typeData);
259 if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
261 if (!typeData.IsListType)
263 if (attributeOverrides != null)
264 atts = attributeOverrides[typeData.Type];
266 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
267 throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
271 atts = new XmlAttributes (typeData.Type);
273 if (atts.XmlRoot != null && root == null)
276 if (atts.XmlType != null)
278 if (atts.XmlType.Namespace != null)
279 typeNamespace = atts.XmlType.Namespace;
281 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
282 defaultXmlType = XmlConvert.EncodeLocalName (atts.XmlType.TypeName);
284 includeInSchema = atts.XmlType.IncludeInSchema;
287 elementName = defaultXmlType;
291 if (root.ElementName.Length != 0)
292 elementName = XmlConvert.EncodeLocalName(root.ElementName);
293 if (root.Namespace != null)
294 rootNamespace = root.Namespace;
295 nullable = root.IsNullable;
298 if (rootNamespace == null) rootNamespace = "";
299 if (typeNamespace == null) typeNamespace = rootNamespace;
302 switch (typeData.SchemaType) {
303 case SchemaTypes.XmlSerializable:
304 map = new XmlSerializableMapping (root, elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
306 case SchemaTypes.Primitive:
307 if (!typeData.IsXsdType)
308 map = new XmlTypeMapping (elementName, rootNamespace,
309 typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
311 map = new XmlTypeMapping (elementName, rootNamespace,
312 typeData, defaultXmlType, typeNamespace);
315 map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
319 map.IncludeInSchema = includeInSchema;
320 map.IsNullable = nullable;
321 relatedMaps.Add (map);
326 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
328 TypeData typeData = TypeTranslator.GetTypeData (type);
329 return ImportClassMapping (typeData, root, defaultNamespace);
332 XmlTypeMapping ImportClassMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
334 Type type = typeData.Type;
336 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
337 if (map != null) return map;
339 if (!allowPrivateTypes)
340 ReflectionHelper.CheckSerializableType (type, false);
342 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
343 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
344 helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
348 ClassMap classMap = new ClassMap ();
349 map.ObjectMap = classMap;
351 var members = GetReflectionMembers (type);
352 bool? isOrderExplicit = null;
353 foreach (XmlReflectionMember rmember in members)
355 if (isOrderExplicit == null)
356 isOrderExplicit = rmember.XmlAttributes.Order >= 0;
357 else if (isOrderExplicit != (rmember.XmlAttributes.Order >= 0))
358 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.");
360 if (isOrderExplicit == true)
361 members.Sort ((m1, m2) => m1.XmlAttributes.Order - m2.XmlAttributes.Order);
363 foreach (XmlReflectionMember rmember in members)
365 string ns = map.XmlTypeNamespace;
366 if (rmember.XmlAttributes.XmlIgnore) continue;
367 if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
368 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
369 ns = bmap.XmlTypeNamespace;
373 XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
374 mem.CheckOptionalValueType (type);
375 classMap.AddMember (mem);
376 } catch (Exception ex) {
377 throw new InvalidOperationException (string.Format (
378 CultureInfo.InvariantCulture, "There was an error" +
379 " reflecting field '{0}'.", rmember.MemberName), ex);
383 // Import extra classes
385 if (type == typeof (object) && includedTypes != null)
387 foreach (Type intype in includedTypes)
388 map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
391 // Register inheritance relations
393 if (type.BaseType != null)
395 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
396 ClassMap cbmap = bmap.ObjectMap as ClassMap;
398 if (type.BaseType != typeof (object)) {
400 if (!cbmap.HasSimpleContent)
401 classMap.SetCanBeSimpleType (false);
404 // At this point, derived classes of this map must be already registered
406 RegisterDerivedMap (bmap, map);
408 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
409 throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
412 ImportIncludedTypes (type, defaultNamespace);
414 if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
416 XmlTypeMapMember mem = classMap.XmlTextCollector;
417 if (mem.TypeData.Type != typeof(string) &&
418 mem.TypeData.Type != typeof(string[]) &&
420 mem.TypeData.Type != typeof(XmlNode[]) &&
422 mem.TypeData.Type != typeof(object[]))
424 throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
430 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
432 map.DerivedTypes.Add (derivedMap);
433 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
435 if (map.BaseMap != null)
436 RegisterDerivedMap (map.BaseMap, derivedMap);
438 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
440 obmap.DerivedTypes.Add (derivedMap);
444 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
446 string typeNamespace = null;
448 XmlAttributes atts = null;
449 if (!typeData.IsListType)
451 if (attributeOverrides != null)
452 atts = attributeOverrides[typeData.Type];
456 atts = new XmlAttributes (typeData.Type);
458 if (atts.XmlType != null)
460 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
461 typeNamespace = atts.XmlType.Namespace;
464 if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
466 if (atts.XmlRoot != null && root == null)
471 if (root.Namespace != null && root.Namespace.Length != 0)
472 return root.Namespace;
475 if (defaultNamespace == null) return "";
476 else return defaultNamespace;
479 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
481 TypeData typeData = TypeTranslator.GetTypeData (type);
482 return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel);
485 XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
487 Type type = typeData.Type;
488 ListMap obmap = new ListMap ();
490 if (!allowPrivateTypes)
491 ReflectionHelper.CheckSerializableType (type, true);
493 if (atts == null) atts = new XmlAttributes();
494 Type itemType = typeData.ListItemType;
496 // warning: byte[][] should not be considered multiarray
497 bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
499 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
501 foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
503 if (att.Namespace != null && att.Form == XmlSchemaForm.Unqualified)
504 throw new InvalidOperationException ("XmlArrayItemAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
505 if (att.NestingLevel != nestingLevel) continue;
506 Type elemType = (att.Type != null) ? att.Type : itemType;
507 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
508 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
509 if (elem.Namespace == null) elem.Namespace = "";
510 elem.Form = att.Form;
511 if (att.Form == XmlSchemaForm.Unqualified)
512 elem.Namespace = string.Empty;
513 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
514 elem.NestingLevel = att.NestingLevel;
517 elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
518 } else if (elem.TypeData.IsComplexType) {
519 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
522 if (att.ElementName.Length != 0) {
523 elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName);
524 } else if (elem.MappedType != null) {
525 elem.ElementName = elem.MappedType.ElementName;
527 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
535 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
537 elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
538 else if (elem.TypeData.IsComplexType)
539 elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
541 if (elem.MappedType != null) {
542 elem.ElementName = elem.MappedType.XmlType;
544 elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType;
547 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
548 elem.IsNullable = CanBeNull (elem.TypeData);
552 obmap.ItemInfo = list;
554 // If there can be different element names (types) in the array, then its name cannot
555 // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
558 if (list.Count > 1) {
559 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
561 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]);
562 if (elem.MappedType != null) {
563 baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
565 baseName = TypeTranslator.GetArrayName (elem.ElementName);
569 // Avoid name colisions
572 string name = baseName;
575 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
576 if (foundMap == null) nameCount = -1;
577 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
578 else name = baseName + (nameCount++);
580 while (nameCount != -1);
582 XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
583 map.ObjectMap = obmap;
585 // Register any of the including types as a derived class of object
586 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
588 XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
589 for (int i = 0; i < includes.Length; i++)
591 Type includedType = includes[i].Type;
592 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
595 // Register this map as a derived class of object
597 helper.RegisterSchemaType (map, name, defaultNamespace);
598 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
603 XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
605 Type type = typeData.Type;
606 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
607 if (map != null) return map;
609 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
610 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
612 if (type.BaseType != null)
614 XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace);
615 if (type.BaseType != typeof (object))
618 RegisterDerivedMap (bmap, map);
624 XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
626 Type type = typeData.Type;
627 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
628 if (map != null) return map;
629 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
630 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
634 // Enum.GetNames is not available in SL API
635 public static System.Collections.Generic.IEnumerable<string> GetEnumNames (Type type)
637 System.Collections.Generic.List<string> names = new System.Collections.Generic.List<string> ();
638 foreach (FieldInfo fi in type.GetFields (BindingFlags.Static | BindingFlags.Public))
643 XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
645 Type type = typeData.Type;
646 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
647 if (map != null) return map;
649 if (!allowPrivateTypes)
650 ReflectionHelper.CheckSerializableType (type, false);
652 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
653 map.IsNullable = false;
654 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
656 ArrayList members = new ArrayList();
658 foreach (string name in GetEnumNames (type)) {
660 string [] names = Enum.GetNames (type);
661 foreach (string name in names) {
663 FieldInfo field = type.GetField (name);
664 string xmlName = null;
665 if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
667 object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
668 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
669 if (xmlName == null) xmlName = name;
670 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
671 members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
674 bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
675 map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
676 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
680 XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
682 Type type = typeData.Type;
683 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
684 if (map != null) return map;
686 if (!allowPrivateTypes)
687 ReflectionHelper.CheckSerializableType (type, false);
689 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
690 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
694 void ImportIncludedTypes (Type type, string defaultNamespace)
696 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
697 for (int n=0; n<includes.Length; n++)
699 Type includedType = includes[n].Type;
700 ImportTypeMapping (includedType, null, defaultNamespace);
704 List<XmlReflectionMember> GetReflectionMembers (Type type)
706 // First we want to find the inheritance hierarchy in reverse order.
707 Type currentType = type;
708 ArrayList typeList = new ArrayList();
709 typeList.Add(currentType);
710 while (currentType != typeof(object))
712 currentType = currentType.BaseType; // Read the base type.
713 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
716 // Read all Fields via reflection.
717 ArrayList fieldList = new ArrayList();
718 FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
720 // This statement ensures fields are ordered starting from the base type.
721 for (int ti=0; ti<typeList.Count; ti++) {
722 for (int i=0; i<tfields.Length; i++) {
723 FieldInfo field = tfields[i];
724 if (field.DeclaringType == typeList[ti])
725 fieldList.Add (field);
730 int currentIndex = 0;
731 foreach (FieldInfo field in tfields)
733 // This statement ensures fields are ordered starting from the base type.
734 if (currentType != field.DeclaringType)
736 currentType = field.DeclaringType;
739 fieldList.Insert(currentIndex++, field);
742 // Read all Properties via reflection.
743 ArrayList propList = new ArrayList();
744 PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
746 // This statement ensures properties are ordered starting from the base type.
747 for (int ti=0; ti<typeList.Count; ti++) {
748 for (int i=0; i<tprops.Length; i++) {
749 PropertyInfo prop = tprops[i];
750 if (!prop.CanRead) continue;
751 if (prop.GetIndexParameters().Length > 0) continue;
752 if (prop.DeclaringType == typeList[ti])
759 foreach (PropertyInfo prop in tprops)
761 // This statement ensures properties are ordered starting from the base type.
762 if (currentType != prop.DeclaringType)
764 currentType = prop.DeclaringType;
767 if (!prop.CanRead) continue;
768 if (prop.GetIndexParameters().Length > 0) continue;
769 propList.Insert(currentIndex++, prop);
772 var members = new List<XmlReflectionMember>();
775 // We now step through the type hierarchy from the base (object) through
776 // to the supplied class, as each step outputting all Fields, and then
777 // all Properties. This is the exact same ordering as .NET 1.0/1.1.
778 foreach (Type t in typeList)
780 // Add any fields matching the current DeclaringType.
781 while (fieldIndex < fieldList.Count)
783 FieldInfo field = (FieldInfo)fieldList[fieldIndex];
784 if (field.DeclaringType==t)
787 XmlAttributes atts = attributeOverrides[type, field.Name];
788 if (atts == null) atts = new XmlAttributes (field);
789 if (atts.XmlIgnore) continue;
790 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
791 member.DeclaringType = field.DeclaringType;
797 // Add any properties matching the current DeclaringType.
798 while (propIndex < propList.Count)
800 PropertyInfo prop = (PropertyInfo)propList[propIndex];
801 if (prop.DeclaringType==t)
804 XmlAttributes atts = attributeOverrides[type, prop.Name];
805 if (atts == null) atts = new XmlAttributes (prop);
806 if (atts.XmlIgnore) continue;
807 if (!prop.CanWrite) {
808 if (prop.PropertyType.IsGenericType && TypeData.GetGenericListItemType (prop.PropertyType) == null) continue; // check this before calling GetTypeData() which raises error for missing Add(). See bug #704813.
809 if (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray) continue;
811 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
812 member.DeclaringType = prop.DeclaringType;
822 private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace)
824 XmlTypeMapMember mapMember;
825 XmlAttributes atts = rmember.XmlAttributes;
826 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
828 if (atts.XmlArray != null) {
829 if (atts.XmlArray.Namespace != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
830 throw new InvalidOperationException ("XmlArrayAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
831 if (typeData.SchemaType != SchemaTypes.Array &&
832 !(typeData.SchemaType == SchemaTypes.Primitive && typeData.Type == typeof (byte [])))
833 throw new InvalidOperationException ("XmlArrayAttribute can be applied to members of array or collection type.");
837 if (atts.XmlAnyAttribute != null)
839 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
840 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
842 mapMember = new XmlTypeMapMemberAnyAttribute();
845 throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
849 if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
851 // no XmlNode type check is done here (seealso: bug #553032).
852 XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
853 member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
858 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
859 mapMember = mapNamespaces;
861 else if (atts.XmlAttribute != null)
865 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
866 throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
868 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
869 if (atts.XmlAttribute.AttributeName.Length == 0)
870 mapAttribute.AttributeName = rmember.MemberName;
872 mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
874 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (mapAttribute.AttributeName);
876 if (typeData.IsComplexType)
877 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, defaultNamespace);
879 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
881 if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
882 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
883 mapAttribute.Form = XmlSchemaForm.Qualified;
884 mapAttribute.Namespace = atts.XmlAttribute.Namespace;
888 mapAttribute.Form = atts.XmlAttribute.Form;
889 if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
890 mapAttribute.Namespace = defaultNamespace;
892 mapAttribute.Namespace = "";
895 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
896 mapMember = mapAttribute;
898 else if (typeData.SchemaType == SchemaTypes.Array)
900 // If the member has a single XmlElementAttribute and the type is the type of the member,
901 // then it is not a flat list
903 if (atts.XmlElements.Count > 1 ||
904 (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
905 (atts.XmlText != null))
909 // check that it does not have XmlArrayAttribute
910 if (atts.XmlArray != null)
911 throw new InvalidOperationException ("XmlArrayAttribute cannot be used with members which also attributed with XmlElementAttribute or XmlTextAttribute.");
913 XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
914 member.ListMap = new ListMap ();
915 member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts);
916 member.ElementInfo = member.ListMap.ItemInfo;
917 member.ListMap.ChoiceMember = member.ChoiceMember;
924 XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
926 // Creates an ElementInfo that identifies the array instance.
927 member.ElementInfo = new XmlTypeMapElementInfoList();
928 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
929 elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName);
930 // note that it could be changed below (when Form is Unqualified)
931 elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
932 elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
933 elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
934 elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
935 elem.ExplicitOrder = (atts.XmlArray != null) ? atts.XmlArray.Order : -1;
936 // This is a bit tricky, but is done
937 // after filling descendant members, so
938 // that array items could be serialized
939 // with proper namespace.
940 if (atts.XmlArray != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
941 elem.Namespace = String.Empty;
943 member.ElementInfo.Add (elem);
951 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
952 member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts);
956 mapMember.DefaultValue = GetDefaultValue (typeData, atts.XmlDefaultValue);
957 mapMember.TypeData = typeData;
958 mapMember.Name = rmember.MemberName;
959 mapMember.IsReturnValue = rmember.IsReturnValue;
963 XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
965 EnumMap choiceEnumMap = null;
966 Type choiceEnumType = null;
968 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
969 ImportTextElementInfo (list, defaultType, member, atts, defaultNamespace);
971 if (atts.XmlChoiceIdentifier != null) {
973 throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context.");
975 member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName;
976 MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public);
978 if (mems.Length == 0)
979 throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls);
981 if (mems[0] is PropertyInfo) {
982 PropertyInfo pi = (PropertyInfo)mems[0];
983 if (!pi.CanWrite || !pi.CanRead)
984 throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write.");
985 choiceEnumType = pi.PropertyType;
987 else choiceEnumType = ((FieldInfo)mems[0]).FieldType;
989 member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType);
991 if (choiceEnumType.IsArray)
992 choiceEnumType = choiceEnumType.GetElementType ();
994 choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap;
995 if (choiceEnumMap == null)
996 throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute.");
999 if (atts.XmlElements.Count == 0 && list.Count == 0)
1001 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
1002 elem.ElementName = defaultName;
1003 elem.Namespace = defaultNamespace;
1004 if (elem.TypeData.IsComplexType)
1005 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
1009 bool multiType = (atts.XmlElements.Count > 1);
1010 foreach (XmlElementAttribute att in atts.XmlElements)
1012 Type elemType = (att.Type != null) ? att.Type : defaultType;
1013 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
1014 elem.Form = att.Form;
1015 if (elem.Form != XmlSchemaForm.Unqualified)
1016 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
1017 elem.IsNullable = att.IsNullable;
1018 elem.ExplicitOrder = att.Order;
1020 if (elem.IsNullable && !elem.TypeData.IsNullable)
1021 throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
1023 if (elem.TypeData.IsComplexType)
1025 if (att.DataType.Length != 0) throw new InvalidOperationException (
1026 string.Format(CultureInfo.InvariantCulture, "'{0}' is "
1027 + "an invalid value for '{1}.{2}' of type '{3}'. "
1028 + "The property may only be specified for primitive types.",
1029 att.DataType, cls.FullName, defaultName,
1030 elem.TypeData.FullTypeName));
1031 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
1034 if (att.ElementName.Length != 0) {
1035 elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName);
1036 } else if (multiType) {
1037 if (elem.MappedType != null) {
1038 elem.ElementName = elem.MappedType.ElementName;
1040 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
1043 elem.ElementName = defaultName;
1046 if (choiceEnumMap != null) {
1047 string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
1049 throw new InvalidOperationException (string.Format (
1050 CultureInfo.InvariantCulture, "Type {0} is missing"
1051 + " enumeration value '{1}' for element '{1} from"
1052 + " namespace '{2}'.", choiceEnumType, elem.ElementName,
1054 elem.ChoiceValue = Enum.Parse (choiceEnumType, cname, false);
1062 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
1064 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
1066 ImportTextElementInfo (list, rmember.MemberType, member, atts, defaultNamespace);
1068 #if !MOONLIGHT // no practical anyElement support
1069 foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
1071 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
1072 if (att.Name.Length != 0)
1074 elem.ElementName = XmlConvert.EncodeLocalName(att.Name);
1075 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
1079 elem.IsUnnamedAnyElement = true;
1080 elem.Namespace = defaultNamespace;
1081 if (att.Namespace != null)
1082 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.");
1084 elem.ExplicitOrder = att.Order;
1091 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts, string defaultNamespace)
1093 if (atts.XmlText != null)
1095 member.IsXmlTextCollector = true;
1096 if (atts.XmlText.Type != null) {
1097 TypeData td = TypeTranslator.GetTypeData (defaultType);
1098 if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
1099 throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
1101 defaultType = atts.XmlText.Type;
1104 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes
1107 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
1109 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
1110 elem.TypeData.SchemaType != SchemaTypes.Enum &&
1111 elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
1112 !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
1114 throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
1116 if (elem.TypeData.IsComplexType)
1117 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
1118 elem.IsTextElement = true;
1119 elem.WrappedElement = false;
1124 bool CanBeNull (TypeData type)
1126 #if !NET_2_0 // idiotic compatibility
1127 if (type.Type == typeof (XmlQualifiedName))
1130 return !type.Type.IsValueType || type.IsNullable;
1133 public void IncludeType (Type type)
1136 throw new ArgumentNullException ("type");
1138 if (includedTypes == null) includedTypes = new ArrayList ();
1139 if (!includedTypes.Contains (type))
1140 includedTypes.Add (type);
1142 if (relatedMaps.Count > 0) {
1143 foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) {
1144 if (map.TypeData.Type == typeof(object))
1145 map.DerivedTypes.Add (ImportTypeMapping (type));
1150 public void IncludeTypes (ICustomAttributeProvider provider)
1152 object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
1154 foreach (XmlIncludeAttribute at in ats)
1155 IncludeType (at.Type);
1158 private object GetDefaultValue (TypeData typeData, object defaultValue)
1160 if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum)
1161 return defaultValue;
1164 string namedValue = (defaultValue as Enum).ToString ("g");
1165 string decimalValue = (defaultValue as Enum).ToString ("d");
1167 // get string representation of enum value
1168 string namedValue = Enum.Format (typeData.Type, defaultValue, "g");
1169 // get decimal representation of enum value
1170 string decimalValue = Enum.Format (typeData.Type, defaultValue, "d");
1172 // if decimal representation matches string representation, then
1173 // the value is not defined in the enum type (as the "g" format
1174 // will return the decimal equivalent of the value if the value
1175 // is not equal to a combination of named enumerated constants
1176 if (namedValue == decimalValue) {
1177 string msg = string.Format (CultureInfo.InvariantCulture,
1178 "Value '{0}' cannot be converted to {1}.", defaultValue,
1179 defaultValue.GetType ().FullName);
1180 throw new InvalidOperationException (msg);
1183 // XmlSerializer expects integral enum value
1184 //return namedValue.Replace (',', ' ');
1185 return defaultValue;
1188 #endregion // Methods