2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.XML / System.Xml.Serialization / XmlReflectionImporter.cs
blob50c430a9346f0b184ec7a80f696ea8f145515c0e
1 //
2 // System.Xml.Serialization.XmlReflectionImporter
3 //
4 // Author:
5 // Tim Coleman (tim@timcoleman.com)
6 // Erik LeBel (eriklebel@yahoo.ca)
7 // Lluis Sanchez Gual (lluis@ximian.com)
8 //
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:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
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";
57 #region Constructors
59 public XmlReflectionImporter ()
60 : this (null, null)
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;
78 else
79 this.initialDefaultNamespace = defaultNamespace;
81 if (attributeOverrides == null)
82 this.attributeOverrides = new XmlAttributeOverrides();
83 else
84 this.attributeOverrides = attributeOverrides;
87 /* void Reset ()
89 helper = new ReflectionHelper();
90 arrayChoiceCount = 1;
94 internal bool AllowPrivateTypes
96 get { return allowPrivateTypes; }
97 set { allowPrivateTypes = value; }
100 #endregion // Constructors
102 #region Methods
104 public XmlMembersMapping ImportMembersMapping (string elementName,
105 string ns,
106 XmlReflectionMember [] members,
107 bool hasWrapperElement)
109 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, true);
112 #if NET_2_0
113 [MonoTODO]
114 public
115 #endif
116 XmlMembersMapping ImportMembersMapping (string elementName,
117 string ns,
118 XmlReflectionMember[] members,
119 bool hasWrapperElement,
120 bool writeAccessors)
122 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, true);
125 #if NET_2_0
126 [MonoTODO]
127 public
128 #endif
129 XmlMembersMapping ImportMembersMapping (string elementName,
130 string ns,
131 XmlReflectionMember[] members,
132 bool hasWrapperElement,
133 bool writeAccessors,
134 bool validate)
136 return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, validate, XmlMappingAccess.Read | XmlMappingAccess.Write);
139 #if NET_2_0
140 [MonoTODO] // FIXME: handle writeAccessors, validate, and mapping access
141 public
142 #endif
143 XmlMembersMapping ImportMembersMapping (string elementName,
144 string ns,
145 XmlReflectionMember[] members,
146 bool hasWrapperElement,
147 bool writeAccessors,
148 bool validate,
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;
166 return mps;
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)
186 if (type == null)
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,
193 defaultNamespace);
196 internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
198 return ImportTypeMapping (typeData, (XmlRootAttribute) null,
199 defaultNamespace);
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;
213 try {
214 XmlTypeMapping map;
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 #if NET_2_0
227 // bug #372780
228 map.SetKey (typeData.Type.ToString ());
229 #endif
230 map.RelatedMaps = relatedMaps;
231 map.Format = SerializationFormat.Literal;
232 Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
233 map.Source = new XmlTypeSerializationSource (typeData.Type, root, attributeOverrides, defaultNamespace, extraTypes);
234 if (allowPrivateTypes) map.Source.CanBeGenerated = false;
235 return map;
236 } catch (InvalidOperationException ex) {
237 throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
238 "There was an error reflecting type '{0}'.", typeData.Type.FullName), ex);
242 XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
244 string rootNamespace = defaultNamespace;
245 string typeNamespace = null;
246 string elementName;
247 bool includeInSchema = true;
248 XmlAttributes atts = null;
249 bool nullable = CanBeNull (typeData);
251 if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
253 if (!typeData.IsListType)
255 if (attributeOverrides != null)
256 atts = attributeOverrides[typeData.Type];
258 if (atts != null && typeData.SchemaType == SchemaTypes.Primitive)
259 throw new InvalidOperationException ("XmlRoot and XmlType attributes may not be specified for the type " + typeData.FullTypeName);
262 if (atts == null)
263 atts = new XmlAttributes (typeData.Type);
265 if (atts.XmlRoot != null && root == null)
266 root = atts.XmlRoot;
268 if (atts.XmlType != null)
270 if (atts.XmlType.Namespace != null)
271 typeNamespace = atts.XmlType.Namespace;
273 if (atts.XmlType.TypeName != null && atts.XmlType.TypeName != string.Empty)
274 defaultXmlType = XmlConvert.EncodeLocalName (atts.XmlType.TypeName);
276 includeInSchema = atts.XmlType.IncludeInSchema;
279 elementName = defaultXmlType;
281 if (root != null)
283 if (root.ElementName.Length != 0)
284 elementName = XmlConvert.EncodeLocalName(root.ElementName);
285 if (root.Namespace != null)
286 rootNamespace = root.Namespace;
287 nullable = root.IsNullable;
290 if (rootNamespace == null) rootNamespace = "";
291 if (typeNamespace == null) typeNamespace = rootNamespace;
293 XmlTypeMapping map;
294 switch (typeData.SchemaType) {
295 case SchemaTypes.XmlSerializable:
296 map = new XmlSerializableMapping (root, elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
297 break;
298 case SchemaTypes.Primitive:
299 if (!typeData.IsXsdType)
300 map = new XmlTypeMapping (elementName, rootNamespace,
301 typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
302 else
303 map = new XmlTypeMapping (elementName, rootNamespace,
304 typeData, defaultXmlType, typeNamespace);
305 break;
306 default:
307 map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
308 break;
311 map.IncludeInSchema = includeInSchema;
312 map.IsNullable = nullable;
313 relatedMaps.Add (map);
315 return map;
318 XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
320 TypeData typeData = TypeTranslator.GetTypeData (type);
321 return ImportClassMapping (typeData, root, defaultNamespace);
324 XmlTypeMapping ImportClassMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
326 Type type = typeData.Type;
328 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
329 if (map != null) return map;
331 if (!allowPrivateTypes)
332 ReflectionHelper.CheckSerializableType (type, false);
334 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
335 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
336 helper.RegisterSchemaType (map, map.XmlType, map.XmlTypeNamespace);
338 // Import members
340 ClassMap classMap = new ClassMap ();
341 map.ObjectMap = classMap;
343 ICollection members = GetReflectionMembers (type);
344 foreach (XmlReflectionMember rmember in members)
346 string ns = map.XmlTypeNamespace;
347 if (rmember.XmlAttributes.XmlIgnore) continue;
348 if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
349 XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
350 ns = bmap.XmlTypeNamespace;
353 try {
354 XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
355 mem.CheckOptionalValueType (type);
356 classMap.AddMember (mem);
357 } catch (Exception ex) {
358 throw new InvalidOperationException (string.Format (
359 CultureInfo.InvariantCulture, "There was an error" +
360 " reflecting field '{0}'.", rmember.MemberName), ex);
364 // Import extra classes
366 if (type == typeof (object) && includedTypes != null)
368 foreach (Type intype in includedTypes)
369 map.DerivedTypes.Add (ImportTypeMapping (intype, defaultNamespace));
372 // Register inheritance relations
374 if (type.BaseType != null)
376 XmlTypeMapping bmap = ImportClassMapping (type.BaseType, root, defaultNamespace);
377 ClassMap cbmap = bmap.ObjectMap as ClassMap;
379 if (type.BaseType != typeof (object)) {
380 map.BaseMap = bmap;
381 if (!cbmap.HasSimpleContent)
382 classMap.SetCanBeSimpleType (false);
385 // At this point, derived classes of this map must be already registered
387 RegisterDerivedMap (bmap, map);
389 if (cbmap.HasSimpleContent && classMap.ElementMembers != null && classMap.ElementMembers.Count != 1)
390 throw new InvalidOperationException (String.Format (errSimple, map.TypeData.TypeName, map.BaseMap.TypeData.TypeName));
393 ImportIncludedTypes (type, defaultNamespace);
395 if (classMap.XmlTextCollector != null && !classMap.HasSimpleContent)
397 XmlTypeMapMember mem = classMap.XmlTextCollector;
398 if (mem.TypeData.Type != typeof(string) &&
399 mem.TypeData.Type != typeof(string[]) &&
400 mem.TypeData.Type != typeof(object[]) &&
401 mem.TypeData.Type != typeof(XmlNode[]))
403 throw new InvalidOperationException (String.Format (errSimple2, map.TypeData.TypeName, mem.Name, mem.TypeData.TypeName));
406 return map;
409 void RegisterDerivedMap (XmlTypeMapping map, XmlTypeMapping derivedMap)
411 map.DerivedTypes.Add (derivedMap);
412 map.DerivedTypes.AddRange (derivedMap.DerivedTypes);
414 if (map.BaseMap != null)
415 RegisterDerivedMap (map.BaseMap, derivedMap);
416 else {
417 XmlTypeMapping obmap = ImportTypeMapping (typeof(object));
418 if (obmap != map)
419 obmap.DerivedTypes.Add (derivedMap);
423 string GetTypeNamespace (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
425 string typeNamespace = null;
427 XmlAttributes atts = null;
428 if (!typeData.IsListType)
430 if (attributeOverrides != null)
431 atts = attributeOverrides[typeData.Type];
434 if (atts == null)
435 atts = new XmlAttributes (typeData.Type);
437 if (atts.XmlType != null)
439 if (atts.XmlType.Namespace != null && atts.XmlType.Namespace.Length != 0 && typeData.SchemaType != SchemaTypes.Enum)
440 typeNamespace = atts.XmlType.Namespace;
443 if (typeNamespace != null && typeNamespace.Length != 0) return typeNamespace;
445 if (atts.XmlRoot != null && root == null)
446 root = atts.XmlRoot;
448 if (root != null)
450 if (root.Namespace != null && root.Namespace.Length != 0)
451 return root.Namespace;
454 if (defaultNamespace == null) return "";
455 else return defaultNamespace;
458 XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
460 TypeData typeData = TypeTranslator.GetTypeData (type);
461 return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel);
464 XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
466 Type type = typeData.Type;
467 ListMap obmap = new ListMap ();
469 if (!allowPrivateTypes)
470 ReflectionHelper.CheckSerializableType (type, true);
472 if (atts == null) atts = new XmlAttributes();
473 Type itemType = typeData.ListItemType;
475 // warning: byte[][] should not be considered multiarray
476 bool isMultiArray = (type.IsArray && (TypeTranslator.GetTypeData(itemType).SchemaType == SchemaTypes.Array) && itemType.IsArray);
478 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
480 foreach (XmlArrayItemAttribute att in atts.XmlArrayItems)
482 if (att.Namespace != null && att.Form == XmlSchemaForm.Unqualified)
483 throw new InvalidOperationException ("XmlArrayItemAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
484 if (att.NestingLevel != nestingLevel) continue;
485 Type elemType = (att.Type != null) ? att.Type : itemType;
486 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData(elemType, att.DataType));
487 elem.Namespace = att.Namespace != null ? att.Namespace : defaultNamespace;
488 if (elem.Namespace == null) elem.Namespace = "";
489 elem.Form = att.Form;
490 if (att.Form == XmlSchemaForm.Unqualified)
491 elem.Namespace = string.Empty;
492 elem.IsNullable = att.IsNullable && CanBeNull (elem.TypeData);
493 elem.NestingLevel = att.NestingLevel;
495 if (isMultiArray) {
496 elem.MappedType = ImportListMapping (elemType, null, elem.Namespace, atts, nestingLevel + 1);
497 } else if (elem.TypeData.IsComplexType) {
498 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
501 if (att.ElementName.Length != 0) {
502 elem.ElementName = XmlConvert.EncodeLocalName (att.ElementName);
503 } else if (elem.MappedType != null) {
504 elem.ElementName = elem.MappedType.ElementName;
505 } else {
506 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
509 list.Add (elem);
512 if (list.Count == 0)
514 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (null, TypeTranslator.GetTypeData (itemType));
515 if (isMultiArray)
516 elem.MappedType = ImportListMapping (itemType, null, defaultNamespace, atts, nestingLevel + 1);
517 else if (elem.TypeData.IsComplexType)
518 elem.MappedType = ImportTypeMapping (itemType, null, defaultNamespace);
520 if (elem.MappedType != null) {
521 elem.ElementName = elem.MappedType.XmlType;
522 } else {
523 elem.ElementName = TypeTranslator.GetTypeData (itemType).XmlType;
526 elem.Namespace = (defaultNamespace != null) ? defaultNamespace : "";
527 elem.IsNullable = CanBeNull (elem.TypeData);
528 list.Add (elem);
531 obmap.ItemInfo = list;
533 // If there can be different element names (types) in the array, then its name cannot
534 // be "ArrayOfXXX" it must be something like ArrayOfChoiceNNN
536 string baseName;
537 if (list.Count > 1) {
538 baseName = "ArrayOfChoice" + (arrayChoiceCount++);
539 } else {
540 XmlTypeMapElementInfo elem = ((XmlTypeMapElementInfo) list[0]);
541 if (elem.MappedType != null) {
542 baseName = TypeTranslator.GetArrayName (elem.MappedType.XmlType);
543 } else {
544 baseName = TypeTranslator.GetArrayName (elem.ElementName);
548 // Avoid name colisions
550 int nameCount = 1;
551 string name = baseName;
553 do {
554 XmlTypeMapping foundMap = helper.GetRegisteredSchemaType (name, defaultNamespace);
555 if (foundMap == null) nameCount = -1;
556 else if (obmap.Equals (foundMap.ObjectMap) && typeData.Type == foundMap.TypeData.Type) return foundMap;
557 else name = baseName + (nameCount++);
559 while (nameCount != -1);
561 XmlTypeMapping map = CreateTypeMapping (typeData, root, name, defaultNamespace);
562 map.ObjectMap = obmap;
564 // Register any of the including types as a derived class of object
565 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
567 XmlTypeMapping objectMapping = ImportTypeMapping (typeof(object));
568 for (int i = 0; i < includes.Length; i++)
570 Type includedType = includes[i].Type;
571 objectMapping.DerivedTypes.Add(ImportTypeMapping (includedType, null, defaultNamespace));
574 // Register this map as a derived class of object
576 helper.RegisterSchemaType (map, name, defaultNamespace);
577 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
579 return map;
582 XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
584 Type type = typeData.Type;
585 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
586 if (map != null) return map;
588 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
589 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
591 if (type.BaseType != null)
593 XmlTypeMapping bmap = ImportTypeMapping (type.BaseType, root, defaultNamespace);
594 if (type.BaseType != typeof (object))
595 map.BaseMap = bmap;
597 RegisterDerivedMap (bmap, map);
600 return map;
603 XmlTypeMapping ImportPrimitiveMapping (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;
608 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
609 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
610 return map;
613 XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
615 Type type = typeData.Type;
616 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
617 if (map != null) return map;
619 if (!allowPrivateTypes)
620 ReflectionHelper.CheckSerializableType (type, false);
622 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
623 map.IsNullable = false;
624 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
626 string [] names = Enum.GetNames (type);
627 ArrayList members = new ArrayList();
628 foreach (string name in names)
630 FieldInfo field = type.GetField (name);
631 string xmlName = null;
632 if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
633 continue;
634 object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
635 if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
636 if (xmlName == null) xmlName = name;
637 long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
638 members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
641 bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
642 map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
643 ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
644 return map;
647 XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
649 Type type = typeData.Type;
650 XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
651 if (map != null) return map;
653 if (!allowPrivateTypes)
654 ReflectionHelper.CheckSerializableType (type, false);
656 map = CreateTypeMapping (typeData, root, null, defaultNamespace);
657 helper.RegisterClrType (map, type, map.XmlTypeNamespace);
658 return map;
661 void ImportIncludedTypes (Type type, string defaultNamespace)
663 XmlIncludeAttribute[] includes = (XmlIncludeAttribute[])type.GetCustomAttributes (typeof (XmlIncludeAttribute), false);
664 for (int n=0; n<includes.Length; n++)
666 Type includedType = includes[n].Type;
667 ImportTypeMapping (includedType, null, defaultNamespace);
671 ICollection GetReflectionMembers (Type type)
673 // First we want to find the inheritance hierarchy in reverse order.
674 Type currentType = type;
675 ArrayList typeList = new ArrayList();
676 typeList.Add(currentType);
677 while (currentType != typeof(object))
679 currentType = currentType.BaseType; // Read the base type.
680 typeList.Insert(0, currentType); // Insert at 0 to reverse the order.
683 // Read all Fields via reflection.
684 ArrayList fieldList = new ArrayList();
685 FieldInfo[] tfields = type.GetFields (BindingFlags.Instance | BindingFlags.Public);
686 #if TARGET_JVM
687 // This statement ensures fields are ordered starting from the base type.
688 for (int ti=0; ti<typeList.Count; ti++) {
689 for (int i=0; i<tfields.Length; i++) {
690 FieldInfo field = tfields[i];
691 if (field.DeclaringType == typeList[ti])
692 fieldList.Add (field);
695 #else
696 currentType = null;
697 int currentIndex = 0;
698 foreach (FieldInfo field in tfields)
700 // This statement ensures fields are ordered starting from the base type.
701 if (currentType != field.DeclaringType)
703 currentType = field.DeclaringType;
704 currentIndex=0;
706 fieldList.Insert(currentIndex++, field);
708 #endif
709 // Read all Properties via reflection.
710 ArrayList propList = new ArrayList();
711 PropertyInfo[] tprops = type.GetProperties (BindingFlags.Instance | BindingFlags.Public);
712 #if TARGET_JVM
713 // This statement ensures properties are ordered starting from the base type.
714 for (int ti=0; ti<typeList.Count; ti++) {
715 for (int i=0; i<tprops.Length; i++) {
716 PropertyInfo prop = tprops[i];
717 if (!prop.CanRead) continue;
718 if (prop.GetIndexParameters().Length > 0) continue;
719 if (prop.DeclaringType == typeList[ti])
720 propList.Add (prop);
723 #else
724 currentType = null;
725 currentIndex = 0;
726 foreach (PropertyInfo prop in tprops)
728 // This statement ensures properties are ordered starting from the base type.
729 if (currentType != prop.DeclaringType)
731 currentType = prop.DeclaringType;
732 currentIndex = 0;
734 if (!prop.CanRead) continue;
735 if (prop.GetIndexParameters().Length > 0) continue;
736 propList.Insert(currentIndex++, prop);
738 #endif
739 ArrayList members = new ArrayList();
740 int fieldIndex=0;
741 int propIndex=0;
742 // We now step through the type hierarchy from the base (object) through
743 // to the supplied class, as each step outputting all Fields, and then
744 // all Properties. This is the exact same ordering as .NET 1.0/1.1.
745 foreach (Type t in typeList)
747 // Add any fields matching the current DeclaringType.
748 while (fieldIndex < fieldList.Count)
750 FieldInfo field = (FieldInfo)fieldList[fieldIndex];
751 if (field.DeclaringType==t)
753 fieldIndex++;
754 XmlAttributes atts = attributeOverrides[type, field.Name];
755 if (atts == null) atts = new XmlAttributes (field);
756 if (atts.XmlIgnore) continue;
757 XmlReflectionMember member = new XmlReflectionMember(field.Name, field.FieldType, atts);
758 member.DeclaringType = field.DeclaringType;
759 members.Add(member);
761 else break;
764 // Add any properties matching the current DeclaringType.
765 while (propIndex < propList.Count)
767 PropertyInfo prop = (PropertyInfo)propList[propIndex];
768 if (prop.DeclaringType==t)
770 propIndex++;
771 XmlAttributes atts = attributeOverrides[type, prop.Name];
772 if (atts == null) atts = new XmlAttributes (prop);
773 if (atts.XmlIgnore) continue;
774 if (!prop.CanWrite && (TypeTranslator.GetTypeData (prop.PropertyType).SchemaType != SchemaTypes.Array || prop.PropertyType.IsArray)) continue;
775 XmlReflectionMember member = new XmlReflectionMember(prop.Name, prop.PropertyType, atts);
776 member.DeclaringType = prop.DeclaringType;
777 members.Add(member);
779 else break;
782 return members;
785 private XmlTypeMapMember CreateMapMember (Type declaringType, XmlReflectionMember rmember, string defaultNamespace)
787 XmlTypeMapMember mapMember;
788 XmlAttributes atts = rmember.XmlAttributes;
789 TypeData typeData = TypeTranslator.GetTypeData (rmember.MemberType);
791 if (atts.XmlArray != null) {
792 if (atts.XmlArray.Namespace != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
793 throw new InvalidOperationException ("XmlArrayAttribute.Form must not be Unqualified when it has an explicit Namespace value.");
794 if (typeData.SchemaType != SchemaTypes.Array &&
795 !(typeData.SchemaType == SchemaTypes.Primitive && typeData.Type == typeof (byte [])))
796 throw new InvalidOperationException ("XmlArrayAttribute can be applied to members of array or collection type.");
799 if (atts.XmlAnyAttribute != null)
801 if ( (rmember.MemberType.FullName == "System.Xml.XmlAttribute[]") ||
802 (rmember.MemberType.FullName == "System.Xml.XmlNode[]") )
804 mapMember = new XmlTypeMapMemberAnyAttribute();
806 else
807 throw new InvalidOperationException ("XmlAnyAttributeAttribute can only be applied to members of type XmlAttribute[] or XmlNode[]");
809 else if (atts.XmlAnyElements != null && atts.XmlAnyElements.Count > 0)
811 // no XmlNode type check is done here (seealso: bug #553032).
812 XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement();
813 member.ElementInfo = ImportAnyElementInfo (defaultNamespace, rmember, member, atts);
814 mapMember = member;
816 else if (atts.Xmlns)
818 XmlTypeMapMemberNamespaces mapNamespaces = new XmlTypeMapMemberNamespaces ();
819 mapMember = mapNamespaces;
821 else if (atts.XmlAttribute != null)
823 // An attribute
825 if (atts.XmlElements != null && atts.XmlElements.Count > 0)
826 throw new Exception ("XmlAttributeAttribute and XmlElementAttribute cannot be applied to the same member");
828 XmlTypeMapMemberAttribute mapAttribute = new XmlTypeMapMemberAttribute ();
829 if (atts.XmlAttribute.AttributeName.Length == 0)
830 mapAttribute.AttributeName = rmember.MemberName;
831 else
832 mapAttribute.AttributeName = atts.XmlAttribute.AttributeName;
834 mapAttribute.AttributeName = XmlConvert.EncodeLocalName (mapAttribute.AttributeName);
836 if (typeData.IsComplexType)
837 mapAttribute.MappedType = ImportTypeMapping (typeData.Type, null, defaultNamespace);
839 if (atts.XmlAttribute.Namespace != null && atts.XmlAttribute.Namespace != defaultNamespace)
841 if (atts.XmlAttribute.Form == XmlSchemaForm.Unqualified)
842 throw new InvalidOperationException ("The Form property may not be 'Unqualified' when an explicit Namespace property is present");
843 mapAttribute.Form = XmlSchemaForm.Qualified;
844 mapAttribute.Namespace = atts.XmlAttribute.Namespace;
846 else
848 mapAttribute.Form = atts.XmlAttribute.Form;
849 if (atts.XmlAttribute.Form == XmlSchemaForm.Qualified)
850 mapAttribute.Namespace = defaultNamespace;
851 else
852 mapAttribute.Namespace = "";
855 typeData = TypeTranslator.GetTypeData(rmember.MemberType, atts.XmlAttribute.DataType);
856 mapMember = mapAttribute;
858 else if (typeData.SchemaType == SchemaTypes.Array)
860 // If the member has a single XmlElementAttribute and the type is the type of the member,
861 // then it is not a flat list
863 if (atts.XmlElements.Count > 1 ||
864 (atts.XmlElements.Count == 1 && atts.XmlElements[0].Type != typeData.Type) ||
865 (atts.XmlText != null))
867 // A flat list
869 // check that it does not have XmlArrayAttribute
870 if (atts.XmlArray != null)
871 throw new InvalidOperationException ("XmlArrayAttribute cannot be used with members which also attributed with XmlElementAttribute or XmlTextAttribute.");
873 XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
874 member.ListMap = new ListMap ();
875 member.ListMap.ItemInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName (rmember.MemberName), defaultNamespace, typeData.ListItemType, member, atts);
876 member.ElementInfo = member.ListMap.ItemInfo;
877 member.ListMap.ChoiceMember = member.ChoiceMember;
878 mapMember = member;
880 else
882 // A list
884 XmlTypeMapMemberList member = new XmlTypeMapMemberList ();
886 // Creates an ElementInfo that identifies the array instance.
887 member.ElementInfo = new XmlTypeMapElementInfoList();
888 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, typeData);
889 elem.ElementName = XmlConvert.EncodeLocalName((atts.XmlArray != null && atts.XmlArray.ElementName.Length != 0) ? atts.XmlArray.ElementName : rmember.MemberName);
890 // note that it could be changed below (when Form is Unqualified)
891 elem.Namespace = (atts.XmlArray != null && atts.XmlArray.Namespace != null) ? atts.XmlArray.Namespace : defaultNamespace;
892 elem.MappedType = ImportListMapping (rmember.MemberType, null, elem.Namespace, atts, 0);
893 elem.IsNullable = (atts.XmlArray != null) ? atts.XmlArray.IsNullable : false;
894 elem.Form = (atts.XmlArray != null) ? atts.XmlArray.Form : XmlSchemaForm.Qualified;
895 // This is a bit tricky, but is done
896 // after filling descendant members, so
897 // that array items could be serialized
898 // with proper namespace.
899 if (atts.XmlArray != null && atts.XmlArray.Form == XmlSchemaForm.Unqualified)
900 elem.Namespace = String.Empty;
902 member.ElementInfo.Add (elem);
903 mapMember = member;
906 else
908 // An element
910 XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
911 member.ElementInfo = ImportElementInfo (declaringType, XmlConvert.EncodeLocalName(rmember.MemberName), defaultNamespace, rmember.MemberType, member, atts);
912 mapMember = member;
915 mapMember.DefaultValue = GetDefaultValue (typeData, atts.XmlDefaultValue);
916 mapMember.TypeData = typeData;
917 mapMember.Name = rmember.MemberName;
918 mapMember.IsReturnValue = rmember.IsReturnValue;
919 return mapMember;
922 XmlTypeMapElementInfoList ImportElementInfo (Type cls, string defaultName, string defaultNamespace, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts)
924 EnumMap choiceEnumMap = null;
925 Type choiceEnumType = null;
927 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
928 ImportTextElementInfo (list, defaultType, member, atts, defaultNamespace);
930 if (atts.XmlChoiceIdentifier != null) {
931 if (cls == null)
932 throw new InvalidOperationException ("XmlChoiceIdentifierAttribute not supported in this context.");
934 member.ChoiceMember = atts.XmlChoiceIdentifier.MemberName;
935 MemberInfo[] mems = cls.GetMember (member.ChoiceMember, BindingFlags.Instance|BindingFlags.Public);
937 if (mems.Length == 0)
938 throw new InvalidOperationException ("Choice member '" + member.ChoiceMember + "' not found in class '" + cls);
940 if (mems[0] is PropertyInfo) {
941 PropertyInfo pi = (PropertyInfo)mems[0];
942 if (!pi.CanWrite || !pi.CanRead)
943 throw new InvalidOperationException ("Choice property '" + member.ChoiceMember + "' must be read/write.");
944 choiceEnumType = pi.PropertyType;
946 else choiceEnumType = ((FieldInfo)mems[0]).FieldType;
948 member.ChoiceTypeData = TypeTranslator.GetTypeData (choiceEnumType);
950 if (choiceEnumType.IsArray)
951 choiceEnumType = choiceEnumType.GetElementType ();
953 choiceEnumMap = ImportTypeMapping (choiceEnumType).ObjectMap as EnumMap;
954 if (choiceEnumMap == null)
955 throw new InvalidOperationException ("The member '" + mems[0].Name + "' is not a valid target for XmlChoiceIdentifierAttribute.");
958 if (atts.XmlElements.Count == 0 && list.Count == 0)
960 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType));
961 elem.ElementName = defaultName;
962 elem.Namespace = defaultNamespace;
963 if (elem.TypeData.IsComplexType)
964 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
965 list.Add (elem);
968 bool multiType = (atts.XmlElements.Count > 1);
969 foreach (XmlElementAttribute att in atts.XmlElements)
971 Type elemType = (att.Type != null) ? att.Type : defaultType;
972 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
973 elem.Form = att.Form;
974 if (elem.Form != XmlSchemaForm.Unqualified)
975 elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
976 elem.IsNullable = att.IsNullable;
978 if (elem.IsNullable && !elem.TypeData.IsNullable)
979 throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
981 if (elem.TypeData.IsComplexType)
983 if (att.DataType.Length != 0) throw new InvalidOperationException (
984 string.Format(CultureInfo.InvariantCulture, "'{0}' is "
985 + "an invalid value for '{1}.{2}' of type '{3}'. "
986 + "The property may only be specified for primitive types.",
987 att.DataType, cls.FullName, defaultName,
988 elem.TypeData.FullTypeName));
989 elem.MappedType = ImportTypeMapping (elemType, null, elem.Namespace);
992 if (att.ElementName.Length != 0) {
993 elem.ElementName = XmlConvert.EncodeLocalName(att.ElementName);
994 } else if (multiType) {
995 if (elem.MappedType != null) {
996 elem.ElementName = elem.MappedType.ElementName;
997 } else {
998 elem.ElementName = TypeTranslator.GetTypeData (elemType).XmlType;
1000 } else {
1001 elem.ElementName = defaultName;
1004 if (choiceEnumMap != null) {
1005 string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
1006 if (cname == null)
1007 throw new InvalidOperationException (string.Format (
1008 CultureInfo.InvariantCulture, "Type {0} is missing"
1009 + " enumeration value '{1}' for element '{1} from"
1010 + " namespace '{2}'.", choiceEnumType, elem.ElementName,
1011 elem.Namespace));
1012 elem.ChoiceValue = Enum.Parse (choiceEnumType, cname);
1015 list.Add (elem);
1017 return list;
1020 XmlTypeMapElementInfoList ImportAnyElementInfo (string defaultNamespace, XmlReflectionMember rmember, XmlTypeMapMemberElement member, XmlAttributes atts)
1022 XmlTypeMapElementInfoList list = new XmlTypeMapElementInfoList();
1024 ImportTextElementInfo (list, rmember.MemberType, member, atts, defaultNamespace);
1026 foreach (XmlAnyElementAttribute att in atts.XmlAnyElements)
1028 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
1029 if (att.Name.Length != 0)
1031 elem.ElementName = XmlConvert.EncodeLocalName(att.Name);
1032 elem.Namespace = (att.Namespace != null) ? att.Namespace : "";
1034 else
1036 elem.IsUnnamedAnyElement = true;
1037 elem.Namespace = defaultNamespace;
1038 if (att.Namespace != null)
1039 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.");
1041 list.Add (elem);
1043 return list;
1046 void ImportTextElementInfo (XmlTypeMapElementInfoList list, Type defaultType, XmlTypeMapMemberElement member, XmlAttributes atts, string defaultNamespace)
1048 if (atts.XmlText != null)
1050 member.IsXmlTextCollector = true;
1051 if (atts.XmlText.Type != null) {
1052 TypeData td = TypeTranslator.GetTypeData (defaultType);
1053 if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
1054 throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
1056 defaultType = atts.XmlText.Type;
1058 if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText); // Nodes must be text nodes
1060 XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
1062 if (elem.TypeData.SchemaType != SchemaTypes.Primitive &&
1063 elem.TypeData.SchemaType != SchemaTypes.Enum &&
1064 elem.TypeData.SchemaType != SchemaTypes.XmlNode &&
1065 !(elem.TypeData.SchemaType == SchemaTypes.Array && elem.TypeData.ListItemTypeData.SchemaType == SchemaTypes.XmlNode)
1067 throw new InvalidOperationException ("XmlText cannot be used to encode complex types");
1069 if (elem.TypeData.IsComplexType)
1070 elem.MappedType = ImportTypeMapping (defaultType, null, defaultNamespace);
1071 elem.IsTextElement = true;
1072 elem.WrappedElement = false;
1073 list.Add (elem);
1077 bool CanBeNull (TypeData type)
1079 #if !NET_2_0 // idiotic compatibility
1080 if (type.Type == typeof (XmlQualifiedName))
1081 return false;
1082 #endif
1083 return !type.Type.IsValueType || type.IsNullable;
1086 public void IncludeType (Type type)
1088 if (type == null)
1089 throw new ArgumentNullException ("type");
1091 if (includedTypes == null) includedTypes = new ArrayList ();
1092 if (!includedTypes.Contains (type))
1093 includedTypes.Add (type);
1095 if (relatedMaps.Count > 0) {
1096 foreach (XmlTypeMapping map in (ArrayList) relatedMaps.Clone ()) {
1097 if (map.TypeData.Type == typeof(object))
1098 map.DerivedTypes.Add (ImportTypeMapping (type));
1103 public void IncludeTypes (ICustomAttributeProvider provider)
1105 object[] ats = provider.GetCustomAttributes (typeof(XmlIncludeAttribute), true);
1107 foreach (XmlIncludeAttribute at in ats)
1108 IncludeType (at.Type);
1111 private object GetDefaultValue (TypeData typeData, object defaultValue)
1113 if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum)
1114 return defaultValue;
1116 // get string representation of enum value
1117 string namedValue = Enum.Format (typeData.Type, defaultValue, "g");
1118 // get decimal representation of enum value
1119 string decimalValue = Enum.Format (typeData.Type, defaultValue, "d");
1121 // if decimal representation matches string representation, then
1122 // the value is not defined in the enum type (as the "g" format
1123 // will return the decimal equivalent of the value if the value
1124 // is not equal to a combination of named enumerated constants
1125 if (namedValue == decimalValue) {
1126 string msg = string.Format (CultureInfo.InvariantCulture,
1127 "Value '{0}' cannot be converted to {1}.", defaultValue,
1128 defaultValue.GetType ().FullName);
1129 throw new InvalidOperationException (msg);
1132 // XmlSerializer expects integral enum value
1133 //return namedValue.Replace (',', ' ');
1134 return defaultValue;
1137 #endregion // Methods