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");
228 map
.SetKey (typeData
.Type
.ToString ());
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;
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;
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
);
263 atts
= new XmlAttributes (typeData
.Type
);
265 if (atts
.XmlRoot
!= null && root
== null)
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
;
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
;
294 switch (typeData
.SchemaType
) {
295 case SchemaTypes
.XmlSerializable
:
296 map
= new XmlSerializableMapping (root
, elementName
, rootNamespace
, typeData
, defaultXmlType
, typeNamespace
);
298 case SchemaTypes
.Primitive
:
299 if (!typeData
.IsXsdType
)
300 map
= new XmlTypeMapping (elementName
, rootNamespace
,
301 typeData
, defaultXmlType
, XmlSerializer
.WsdlTypesNamespace
);
303 map
= new XmlTypeMapping (elementName
, rootNamespace
,
304 typeData
, defaultXmlType
, typeNamespace
);
307 map
= new XmlTypeMapping (elementName
, rootNamespace
, typeData
, defaultXmlType
, typeNamespace
);
311 map
.IncludeInSchema
= includeInSchema
;
312 map
.IsNullable
= nullable
;
313 relatedMaps
.Add (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
);
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
;
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)) {
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
));
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
);
417 XmlTypeMapping obmap
= ImportTypeMapping (typeof(object));
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
];
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)
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
;
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
;
506 elem
.ElementName
= TypeTranslator
.GetTypeData (elemType
).XmlType
;
514 XmlTypeMapElementInfo elem
= new XmlTypeMapElementInfo (null, TypeTranslator
.GetTypeData (itemType
));
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
;
523 elem
.ElementName
= TypeTranslator
.GetTypeData (itemType
).XmlType
;
526 elem
.Namespace
= (defaultNamespace
!= null) ? defaultNamespace
: "";
527 elem
.IsNullable
= CanBeNull (elem
.TypeData
);
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
537 if (list
.Count
> 1) {
538 baseName
= "ArrayOfChoice" + (arrayChoiceCount
++);
540 XmlTypeMapElementInfo elem
= ((XmlTypeMapElementInfo
) list
[0]);
541 if (elem
.MappedType
!= null) {
542 baseName
= TypeTranslator
.GetArrayName (elem
.MappedType
.XmlType
);
544 baseName
= TypeTranslator
.GetArrayName (elem
.ElementName
);
548 // Avoid name colisions
551 string name
= baseName
;
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
);
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))
597 RegisterDerivedMap (bmap
, 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
);
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))
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
);
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
);
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
);
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
);
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
;
706 fieldList
.Insert(currentIndex
++, field
);
709 // Read all Properties via reflection.
710 ArrayList propList
= new ArrayList();
711 PropertyInfo
[] tprops
= type
.GetProperties (BindingFlags
.Instance
| BindingFlags
.Public
);
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
])
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
;
734 if (!prop
.CanRead
) continue;
735 if (prop
.GetIndexParameters().Length
> 0) continue;
736 propList
.Insert(currentIndex
++, prop
);
739 ArrayList members
= new ArrayList();
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
)
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
;
764 // Add any properties matching the current DeclaringType.
765 while (propIndex
< propList
.Count
)
767 PropertyInfo prop
= (PropertyInfo
)propList
[propIndex
];
768 if (prop
.DeclaringType
==t
)
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
;
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();
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
);
818 XmlTypeMapMemberNamespaces mapNamespaces
= new XmlTypeMapMemberNamespaces ();
819 mapMember
= mapNamespaces
;
821 else if (atts
.XmlAttribute
!= null)
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
;
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
;
848 mapAttribute
.Form
= atts
.XmlAttribute
.Form
;
849 if (atts
.XmlAttribute
.Form
== XmlSchemaForm
.Qualified
)
850 mapAttribute
.Namespace
= defaultNamespace
;
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))
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
;
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
);
910 XmlTypeMapMemberElement member
= new XmlTypeMapMemberElement ();
911 member
.ElementInfo
= ImportElementInfo (declaringType
, XmlConvert
.EncodeLocalName(rmember
.MemberName
), defaultNamespace
, rmember
.MemberType
, member
, atts
);
915 mapMember
.DefaultValue
= GetDefaultValue (typeData
, atts
.XmlDefaultValue
);
916 mapMember
.TypeData
= typeData
;
917 mapMember
.Name
= rmember
.MemberName
;
918 mapMember
.IsReturnValue
= rmember
.IsReturnValue
;
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) {
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
);
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
;
998 elem
.ElementName
= TypeTranslator
.GetTypeData (elemType
).XmlType
;
1001 elem
.ElementName
= defaultName
;
1004 if (choiceEnumMap
!= null) {
1005 string cname
= choiceEnumMap
.GetEnumName (choiceEnumType
.FullName
, elem
.ElementName
);
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
,
1012 elem
.ChoiceValue
= Enum
.Parse (choiceEnumType
, cname
);
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
: "";
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.");
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;
1077 bool CanBeNull (TypeData type
)
1079 #if !NET_2_0 // idiotic compatibility
1080 if (type
.Type
== typeof (XmlQualifiedName
))
1083 return !type
.Type
.IsValueType
|| type
.IsNullable
;
1086 public void IncludeType (Type type
)
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