2 // System.Xml.Serialization.SoapReflectionImporter
5 // Tim Coleman (tim@timcoleman.com)
7 // Copyright (C) Tim Coleman, 2002
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Reflection
;
33 using System
.Xml
.Schema
;
34 using System
.Collections
;
36 namespace System
.Xml
.Serialization
{
37 public class SoapReflectionImporter
{
39 SoapAttributeOverrides attributeOverrides
;
40 string initialDefaultNamespace
;
41 ArrayList includedTypes
;
42 ArrayList relatedMaps
= new ArrayList ();
43 ReflectionHelper helper
= new ReflectionHelper();
47 public SoapReflectionImporter (): this (null, null)
51 public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides
): this (attributeOverrides
, null)
55 public SoapReflectionImporter (string defaultNamespace
): this (null, defaultNamespace
)
59 public SoapReflectionImporter (SoapAttributeOverrides attributeOverrides
, string defaultNamespace
)
61 if (defaultNamespace
== null) initialDefaultNamespace
= String
.Empty
;
62 else initialDefaultNamespace
= defaultNamespace
;
64 if (attributeOverrides
== null) this.attributeOverrides
= new SoapAttributeOverrides();
65 else this.attributeOverrides
= attributeOverrides
;
68 #endregion // Constructors
72 public XmlMembersMapping
ImportMembersMapping (string elementName
, string ns
, XmlReflectionMember
[] members
)
74 return ImportMembersMapping (elementName
, ns
, members
, true, true, false);
77 public XmlMembersMapping
ImportMembersMapping (string elementName
, string ns
, XmlReflectionMember
[] members
, bool hasWrapperElement
, bool writeAccessors
)
79 return ImportMembersMapping (elementName
, ns
, members
, hasWrapperElement
, writeAccessors
, false);
82 public XmlMembersMapping
ImportMembersMapping (string elementName
, string ns
, XmlReflectionMember
[] members
, bool hasWrapperElement
, bool writeAccessors
, bool validate
)
84 XmlMemberMapping
[] mapping
= new XmlMemberMapping
[members
.Length
];
85 for (int n
=0; n
<members
.Length
; n
++)
87 XmlTypeMapMember mapMem
= CreateMapMember (members
[n
], ns
);
88 mapping
[n
] = new XmlMemberMapping (members
[n
].MemberName
, ns
, mapMem
, true);
90 XmlMembersMapping mps
= new XmlMembersMapping (elementName
, ns
, hasWrapperElement
, writeAccessors
, mapping
);
91 mps
.RelatedMaps
= relatedMaps
;
92 mps
.Format
= SerializationFormat
.Encoded
;
93 Type
[] extraTypes
= includedTypes
!= null ? (Type
[])includedTypes
.ToArray(typeof(Type
)) : null;
94 mps
.Source
= new MembersSerializationSource (elementName
, hasWrapperElement
, members
, writeAccessors
, false, null, extraTypes
);
98 public XmlTypeMapping
ImportTypeMapping (Type type
)
100 return ImportTypeMapping (type
, null);
103 public XmlTypeMapping
ImportTypeMapping (Type type
, string defaultNamespace
)
106 throw new ArgumentNullException ("type");
108 if (type
== typeof (void))
109 throw new InvalidOperationException ("Type " + type
.Name
+ " may not be serialized.");
111 if (defaultNamespace
== null) defaultNamespace
= initialDefaultNamespace
;
112 if (defaultNamespace
== null) defaultNamespace
= string.Empty
;
115 switch (TypeTranslator
.GetTypeData(type
).SchemaType
)
117 case SchemaTypes
.Class
: map
= ImportClassMapping (type
, defaultNamespace
); break;
118 case SchemaTypes
.Array
: map
= ImportListMapping (type
, defaultNamespace
); break;
119 case SchemaTypes
.XmlNode
: throw CreateTypeException (type
);
120 case SchemaTypes
.Primitive
: map
= ImportPrimitiveMapping (type
, defaultNamespace
); break;
121 case SchemaTypes
.Enum
: map
= ImportEnumMapping (type
, defaultNamespace
); break;
122 case SchemaTypes
.XmlSerializable
:
123 default: throw new NotSupportedException ("Type " + type
.FullName
+ " not supported for XML serialization");
125 map
.RelatedMaps
= relatedMaps
;
126 map
.Format
= SerializationFormat
.Encoded
;
127 Type
[] extraTypes
= includedTypes
!= null ? (Type
[])includedTypes
.ToArray(typeof(Type
)) : null;
128 map
.Source
= new SoapTypeSerializationSource (type
, attributeOverrides
, defaultNamespace
, extraTypes
);
132 XmlTypeMapping
CreateTypeMapping (TypeData typeData
, string defaultXmlType
, string defaultNamespace
)
134 string membersNamespace
= defaultNamespace
;
135 bool includeInSchema
= true;
137 SoapAttributes atts
= null;
138 if (defaultXmlType
== null) defaultXmlType
= typeData
.XmlType
;
140 if (!typeData
.IsListType
)
142 if (attributeOverrides
!= null)
143 atts
= attributeOverrides
[typeData
.Type
];
145 if (atts
!= null && typeData
.SchemaType
== SchemaTypes
.Primitive
)
146 throw new InvalidOperationException ("SoapType attribute may not be specified for the type " + typeData
.FullTypeName
);
150 atts
= new SoapAttributes (typeData
.Type
);
152 if (atts
.SoapType
!= null)
154 if (atts
.SoapType
.Namespace
!= null && atts
.SoapType
.Namespace
!= string.Empty
)
155 membersNamespace
= atts
.SoapType
.Namespace
;
157 if (atts
.SoapType
.TypeName
!= null && atts
.SoapType
.TypeName
!= string.Empty
)
158 defaultXmlType
= atts
.SoapType
.TypeName
;
160 includeInSchema
= atts
.SoapType
.IncludeInSchema
;
163 if (membersNamespace
== null) membersNamespace
= "";
164 XmlTypeMapping map
= new XmlTypeMapping (defaultXmlType
, membersNamespace
, typeData
, defaultXmlType
, membersNamespace
);
165 map
.IncludeInSchema
= includeInSchema
;
166 relatedMaps
.Add (map
);
171 XmlTypeMapping
ImportClassMapping (Type type
, string defaultNamespace
)
173 if (type
.IsValueType
) throw CreateStructException (type
);
174 if (type
== typeof (object)) defaultNamespace
= XmlSchema
.Namespace
;
176 ReflectionHelper
.CheckSerializableType (type
, false);
178 TypeData typeData
= TypeTranslator
.GetTypeData (type
);
179 XmlTypeMapping map
= helper
.GetRegisteredClrType (type
, GetTypeNamespace (typeData
, defaultNamespace
));
180 if (map
!= null) return map
;
182 map
= CreateTypeMapping (typeData
, null, defaultNamespace
);
183 helper
.RegisterClrType (map
, type
, map
.Namespace
);
184 map
.MultiReferenceType
= true;
186 ClassMap classMap
= new ClassMap ();
187 map
.ObjectMap
= classMap
;
193 ICollection members
= GetReflectionMembers (type
);
194 foreach (XmlReflectionMember rmember
in members
)
196 if (rmember
.SoapAttributes
.SoapIgnore
) continue;
197 classMap
.AddMember (CreateMapMember (rmember
, map
.Namespace
));
202 throw helper
.CreateError (map
, ex
.Message
);
205 // Import included classes
207 SoapIncludeAttribute
[] includes
= (SoapIncludeAttribute
[])type
.GetCustomAttributes (typeof (SoapIncludeAttribute
), false);
208 for (int n
=0; n
<includes
.Length
; n
++)
210 Type includedType
= includes
[n
].Type
;
211 ImportTypeMapping (includedType
);
214 if (type
== typeof (object) && includedTypes
!= null)
216 foreach (Type intype
in includedTypes
)
217 map
.DerivedTypes
.Add (ImportTypeMapping (intype
));
220 // Register inheritance relations
222 if (type
.BaseType
!= null)
224 XmlTypeMapping bmap
= ImportClassMapping (type
.BaseType
, defaultNamespace
);
226 if (type
.BaseType
!= typeof (object))
229 // At this point, derived classes of this map must be already registered
231 RegisterDerivedMap (bmap
, map
);
237 void RegisterDerivedMap (XmlTypeMapping map
, XmlTypeMapping derivedMap
)
239 map
.DerivedTypes
.Add (derivedMap
);
240 map
.DerivedTypes
.AddRange (derivedMap
.DerivedTypes
);
242 if (map
.BaseMap
!= null)
243 RegisterDerivedMap (map
.BaseMap
, derivedMap
);
245 XmlTypeMapping obmap
= ImportTypeMapping (typeof(object));
247 obmap
.DerivedTypes
.Add (derivedMap
);
251 string GetTypeNamespace (TypeData typeData
, string defaultNamespace
)
253 string membersNamespace
= defaultNamespace
;
255 SoapAttributes atts
= null;
257 if (!typeData
.IsListType
)
259 if (attributeOverrides
!= null)
260 atts
= attributeOverrides
[typeData
.Type
];
264 atts
= new SoapAttributes (typeData
.Type
);
266 if (atts
.SoapType
!= null)
268 if (atts
.SoapType
.Namespace
!= null && atts
.SoapType
.Namespace
!= string.Empty
)
269 membersNamespace
= atts
.SoapType
.Namespace
;
272 if (membersNamespace
== null) return "";
273 else return membersNamespace
;
276 XmlTypeMapping
ImportListMapping (Type type
, string defaultNamespace
)
278 TypeData typeData
= TypeTranslator
.GetTypeData (type
);
279 XmlTypeMapping map
= helper
.GetRegisteredClrType (type
, XmlSerializer
.EncodingNamespace
);
280 if (map
!= null) return map
;
282 ListMap obmap
= new ListMap ();
283 TypeData itemTypeData
= typeData
.ListItemTypeData
;
285 map
= CreateTypeMapping (typeData
, "Array", XmlSerializer
.EncodingNamespace
);
286 helper
.RegisterClrType (map
, type
, XmlSerializer
.EncodingNamespace
);
287 map
.MultiReferenceType
= true;
288 map
.ObjectMap
= obmap
;
290 XmlTypeMapElementInfo elem
= new XmlTypeMapElementInfo (null, itemTypeData
);
292 if (elem
.TypeData
.IsComplexType
) {
293 elem
.MappedType
= ImportTypeMapping (typeData
.ListItemType
, defaultNamespace
);
294 elem
.TypeData
= elem
.MappedType
.TypeData
;
297 elem
.ElementName
= "Item";
298 elem
.Namespace
= string.Empty
;
299 elem
.IsNullable
= true; // By default, items are nullable
301 XmlTypeMapElementInfoList list
= new XmlTypeMapElementInfoList();
304 obmap
.ItemInfo
= list
;
305 XmlTypeMapping objMap
= ImportTypeMapping (typeof(object), defaultNamespace
);
306 objMap
.DerivedTypes
.Add (map
);
308 // Register any of the including types as a derived class of object
309 SoapIncludeAttribute
[] includes
= (SoapIncludeAttribute
[])type
.GetCustomAttributes (typeof (SoapIncludeAttribute
), false);
310 for (int i
= 0; i
< includes
.Length
; i
++)
312 Type includedType
= includes
[i
].Type
;
313 objMap
.DerivedTypes
.Add(ImportTypeMapping (includedType
, defaultNamespace
));
319 XmlTypeMapping
ImportPrimitiveMapping (Type type
, string defaultNamespace
)
321 TypeData typeData
= TypeTranslator
.GetTypeData (type
);
322 XmlTypeMapping map
= helper
.GetRegisteredClrType (type
, GetTypeNamespace (typeData
, defaultNamespace
));
323 if (map
!= null) return map
;
324 map
= CreateTypeMapping (typeData
, null, defaultNamespace
);
325 helper
.RegisterClrType (map
, type
, map
.Namespace
);
330 XmlTypeMapping
ImportEnumMapping (Type type
, string defaultNamespace
)
332 TypeData typeData
= TypeTranslator
.GetTypeData (type
);
333 XmlTypeMapping map
= helper
.GetRegisteredClrType (type
, GetTypeNamespace (typeData
, defaultNamespace
));
334 if (map
!= null) return map
;
336 ReflectionHelper
.CheckSerializableType (type
, false);
338 map
= CreateTypeMapping (typeData
, null, defaultNamespace
);
339 helper
.RegisterClrType (map
, type
, map
.Namespace
);
341 map
.MultiReferenceType
= true;
343 string [] names
= Enum
.GetNames (type
);
344 EnumMap
.EnumMapMember
[] members
= new EnumMap
.EnumMapMember
[names
.Length
];
345 for (int n
=0; n
<names
.Length
; n
++)
347 MemberInfo
[] mem
= type
.GetMember (names
[n
]);
348 string xmlName
= names
[n
];
349 object[] atts
= mem
[0].GetCustomAttributes (typeof(SoapEnumAttribute
), false);
350 if (atts
.Length
> 0) xmlName
= ((SoapEnumAttribute
)atts
[0]).Name
;
351 members
[n
] = new EnumMap
.EnumMapMember (xmlName
, names
[n
]);
354 bool isFlags
= type
.GetCustomAttributes (typeof(FlagsAttribute
),false).Length
> 0;
355 map
.ObjectMap
= new EnumMap (members
, isFlags
);
356 ImportTypeMapping (typeof(object), defaultNamespace
).DerivedTypes
.Add (map
);
360 ICollection
GetReflectionMembers (Type type
)
362 ArrayList members
= new ArrayList();
363 PropertyInfo
[] properties
= type
.GetProperties (BindingFlags
.Instance
| BindingFlags
.Public
);
364 foreach (PropertyInfo prop
in properties
)
366 if (!prop
.CanRead
) continue;
367 if (!prop
.CanWrite
&& TypeTranslator
.GetTypeData (prop
.PropertyType
).SchemaType
!= SchemaTypes
.Array
)
370 SoapAttributes atts
= attributeOverrides
[type
, prop
.Name
];
371 if (atts
== null) atts
= new SoapAttributes (prop
);
372 if (atts
.SoapIgnore
) continue;
373 XmlReflectionMember member
= new XmlReflectionMember(prop
.Name
, prop
.PropertyType
, atts
);
374 members
.Add (member
);
377 FieldInfo
[] fields
= type
.GetFields (BindingFlags
.Instance
| BindingFlags
.Public
);
378 foreach (FieldInfo field
in fields
)
380 SoapAttributes atts
= attributeOverrides
[type
, field
.Name
];
381 if (atts
== null) atts
= new SoapAttributes (field
);
382 if (atts
.SoapIgnore
) continue;
383 XmlReflectionMember member
= new XmlReflectionMember(field
.Name
, field
.FieldType
, atts
);
384 members
.Add (member
);
389 private XmlTypeMapMember
CreateMapMember (XmlReflectionMember rmember
, string defaultNamespace
)
391 XmlTypeMapMember mapMember
;
392 SoapAttributes atts
= rmember
.SoapAttributes
;
393 TypeData typeData
= TypeTranslator
.GetTypeData (rmember
.MemberType
);
395 if (atts
.SoapAttribute
!= null)
399 if (atts
.SoapElement
!= null)
400 throw new Exception ("SoapAttributeAttribute and SoapElementAttribute cannot be applied to the same member");
402 XmlTypeMapMemberAttribute mapAttribute
= new XmlTypeMapMemberAttribute ();
403 if (atts
.SoapAttribute
.AttributeName
== null)
404 mapAttribute
.AttributeName
= rmember
.MemberName
;
406 mapAttribute
.AttributeName
= atts
.SoapAttribute
.AttributeName
;
408 mapAttribute
.Namespace
= (atts
.SoapAttribute
.Namespace
!= null) ? atts
.SoapAttribute
.Namespace
: "";
409 if (typeData
.IsComplexType
)
410 mapAttribute
.MappedType
= ImportTypeMapping (typeData
.Type
, defaultNamespace
);
412 typeData
= TypeTranslator
.GetTypeData (rmember
.MemberType
, atts
.SoapAttribute
.DataType
);
413 mapMember
= mapAttribute
;
417 if (typeData
.SchemaType
== SchemaTypes
.Array
) mapMember
= new XmlTypeMapMemberList ();
418 else mapMember
= new XmlTypeMapMemberElement ();
420 if (atts
.SoapElement
!= null && atts
.SoapElement
.DataType
!= null)
421 typeData
= TypeTranslator
.GetTypeData (rmember
.MemberType
, atts
.SoapElement
.DataType
);
423 // Creates an ElementInfo that identifies the element
424 XmlTypeMapElementInfoList infoList
= new XmlTypeMapElementInfoList();
425 XmlTypeMapElementInfo elem
= new XmlTypeMapElementInfo (mapMember
, typeData
);
427 elem
.ElementName
= (atts
.SoapElement
!= null && atts
.SoapElement
.ElementName
!= null) ? atts
.SoapElement
.ElementName
: rmember
.MemberName
;
428 elem
.Namespace
= string.Empty
;
429 elem
.IsNullable
= (atts
.SoapElement
!= null) ? atts
.SoapElement
.IsNullable
: false;
430 if (typeData
.IsComplexType
)
431 elem
.MappedType
= ImportTypeMapping (typeData
.Type
, defaultNamespace
);
434 ((XmlTypeMapMemberElement
)mapMember
).ElementInfo
= infoList
;
437 mapMember
.TypeData
= typeData
;
438 mapMember
.Name
= rmember
.MemberName
;
439 mapMember
.IsReturnValue
= rmember
.IsReturnValue
;
443 public void IncludeType (Type type
)
446 throw new ArgumentNullException ("type");
448 if (includedTypes
== null) includedTypes
= new ArrayList ();
449 if (!includedTypes
.Contains (type
))
450 includedTypes
.Add (type
);
453 public void IncludeTypes (ICustomAttributeProvider provider
)
455 object[] ats
= provider
.GetCustomAttributes (typeof(SoapIncludeAttribute
), true);
456 foreach (SoapIncludeAttribute at
in ats
)
457 IncludeType (at
.Type
);
460 Exception
CreateTypeException (Type type
)
462 return new NotSupportedException ("The type " + type
.FullName
+ " may not be serialized with SOAP-encoded messages. Set the Use for your message to Literal");
465 Exception
CreateStructException (Type type
)
467 return new NotSupportedException ("Cannot serialize " + type
.FullName
+ ". Nested structs are not supported with encoded SOAP");
473 #endregion // Methods