2010-04-15 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Runtime.Serialization / System.Runtime.Serialization / SerializationMap.cs
blob992b1286841d8dc6e19bc2376339b4a387e16ef6
1 //
2 // SerializationMap.cs
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Ankit Jain <JAnkit@novell.com>
7 // Duncan Mak (duncan@ximian.com)
8 // Eyal Alaluf (eyala@mainsoft.com)
9 //
10 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
11 // Copyright (C) 2006 Novell, Inc. http://www.novell.com
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 #if NET_2_0
33 using System;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.ObjectModel;
37 using System.Linq;
38 using System.Reflection;
39 using System.Xml;
40 using System.Xml.Schema;
41 using System.Xml.Serialization;
43 using QName = System.Xml.XmlQualifiedName;
45 namespace System.Runtime.Serialization
48 XmlFormatter implementation design inference:
50 type definitions:
51 - No XML Schema types are directly used. There are some maps from
52 xs:blahType to ms:blahType where the namespaceURI for prefix "ms" is
53 "http://schemas.microsoft.com/2003/10/Serialization/" .
55 serializable types:
56 - An object being serialized 1) must be of type System.Object, or
57 2) must be null, or 3) must have either a [DataContract] attribute
58 or a [Serializable] attribute to be serializable.
59 - When the object is either of type System.Object or null, then the
60 XML type is "anyType".
61 - When the object is [Serializable], then the runtime-serialization
62 compatible object graph is written.
63 - Otherwise the serialization is based on contract attributes.
64 ([Serializable] takes precedence).
66 type derivation:
67 - For type A to be serializable, the base type B of A must be
68 serializable.
69 - If a type which is [Serializable] and whose base type has a
70 [DataContract], then for base type members [DataContract] is taken.
71 - It is vice versa i.e. if the base type is [Serializable] and the
72 derived type has a [DataContract], then [Serializable] takes place
73 for base members.
75 known type collection:
76 - It internally manages mapping store keyed by contract QNames.
77 KnownTypeCollection.Add() checks if the same QName contract already
78 exists (and raises InvalidOperationException if required).
81 internal abstract partial class SerializationMap
83 public const BindingFlags AllInstanceFlags =
84 BindingFlags.Public | BindingFlags.NonPublic |
85 BindingFlags.Instance;
87 public readonly KnownTypeCollection KnownTypes;
88 public readonly Type RuntimeType;
89 public bool IsReference; // new in 3.5 SP1
90 public List<DataMemberInfo> Members;
91 #if !NET_2_1
92 XmlSchemaSet schema_set;
93 #endif
95 //FIXME FIXME
96 Dictionary<Type, QName> qname_table = new Dictionary<Type, QName> ();
98 protected SerializationMap (
99 Type type, QName qname, KnownTypeCollection knownTypes)
101 KnownTypes = knownTypes;
102 RuntimeType = type;
103 if (qname.Namespace == null)
104 qname = new QName (qname.Name,
105 KnownTypeCollection.DefaultClrNamespaceBase + type.Namespace);
107 XmlName = qname;
108 Members = new List<DataMemberInfo> ();
111 public virtual bool OutputXsiType {
112 get { return true; }
115 public QName XmlName { get; set; }
117 public CollectionDataContractAttribute GetCollectionDataContractAttribute (Type type)
119 object [] atts = type.GetCustomAttributes (
120 typeof (CollectionDataContractAttribute), false);
121 return atts.Length == 0 ? null : (CollectionDataContractAttribute) atts [0];
124 public DataMemberAttribute GetDataMemberAttribute (
125 MemberInfo mi)
127 object [] atts = mi.GetCustomAttributes (
128 typeof (DataMemberAttribute), false);
129 if (atts.Length == 0)
130 return null;
131 return (DataMemberAttribute) atts [0];
134 bool IsPrimitive (Type type)
136 return (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object));
139 #if !NET_2_1
140 /* Returns the XmlSchemaType AND adds it to @schemas */
141 public virtual XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
143 if (IsPrimitive (RuntimeType))
144 return null;
146 if (generated_schema_types.ContainsKey (XmlName)) // Caching
147 return generated_schema_types [XmlName] as XmlSchemaType;
149 XmlSchemaComplexType complex_type = null;
151 complex_type = new XmlSchemaComplexType ();
152 complex_type.Name = XmlName.Name;
153 generated_schema_types [XmlName] = complex_type;
155 if (RuntimeType.BaseType == typeof (object)) {
156 complex_type.Particle = GetSequence (schemas, generated_schema_types);
157 } else {
158 //Has a non-System.Object base class
159 XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension ();
160 XmlSchemaComplexContent content = new XmlSchemaComplexContent ();
162 complex_type.ContentModel = content;
163 content.Content = extension;
165 KnownTypes.Add (RuntimeType.BaseType);
166 SerializationMap map = KnownTypes.FindUserMap (RuntimeType.BaseType);
167 //FIXME: map == null ?
168 map.GetSchemaType (schemas, generated_schema_types);
170 extension.Particle = GetSequence (schemas, generated_schema_types);
171 extension.BaseTypeName = GetQualifiedName (RuntimeType.BaseType);
174 XmlSchemaElement schemaElement = GetSchemaElement (XmlName, complex_type);
175 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
176 schema.Items.Add (complex_type);
177 schema.Items.Add (schemaElement);
178 schemas.Reprocess (schema);
180 return complex_type;
183 /* Returns the <xs:sequence> for the data members */
184 XmlSchemaSequence GetSequence (XmlSchemaSet schemas,
185 Dictionary<QName, XmlSchemaType> generated_schema_types)
187 List<DataMemberInfo> members = GetMembers ();
189 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
190 XmlSchemaSequence sequence = new XmlSchemaSequence ();
191 foreach (DataMemberInfo dmi in members) {
192 // delegates are not supported.
193 if (!dmi.MemberType.IsAbstract && typeof (System.Delegate).IsAssignableFrom (dmi.MemberType))
194 continue;
196 XmlSchemaElement element = new XmlSchemaElement ();
197 element.Name = dmi.XmlName;
199 KnownTypes.Add (dmi.MemberType);
200 SerializationMap map = KnownTypes.FindUserMap (dmi.MemberType);
201 if (map != null) {
202 XmlSchemaType schema_type = map.GetSchemaType (schemas, generated_schema_types);
203 if (schema_type is XmlSchemaComplexType)
204 element.IsNillable = true;
205 } else {
206 //Primitive type
207 if (dmi.MemberType == typeof (string))
208 element.IsNillable = true;
211 element.MinOccurs = 0;
213 element.SchemaTypeName = GetQualifiedName (dmi.MemberType);
214 AddImport (schema, element.SchemaTypeName.Namespace);
216 sequence.Items.Add (element);
219 schemas.Reprocess (schema);
220 return sequence;
223 //FIXME: Replace with a dictionary ?
224 void AddImport (XmlSchema schema, string ns)
226 if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
227 return;
229 foreach (XmlSchemaObject o in schema.Includes) {
230 XmlSchemaImport import = o as XmlSchemaImport;
231 if (import == null)
232 continue;
233 if (import.Namespace == ns)
234 return;
237 XmlSchemaImport imp = new XmlSchemaImport ();
238 imp.Namespace = ns;
239 schema.Includes.Add (imp);
241 #endif
243 //Returns list of data members for this type ONLY
244 public virtual List<DataMemberInfo> GetMembers ()
246 throw new NotImplementedException (String.Format ("Implement me for {0}", this));
249 #if !NET_2_1
250 protected XmlSchemaElement GetSchemaElement (QName qname, XmlSchemaType schemaType)
252 XmlSchemaElement schemaElement = new XmlSchemaElement ();
253 schemaElement.Name = qname.Name;
254 schemaElement.SchemaTypeName = qname;
256 if (schemaType is XmlSchemaComplexType)
257 schemaElement.IsNillable = true;
259 return schemaElement;
262 protected XmlSchema GetSchema (XmlSchemaSet schemas, string ns)
264 ICollection colln = schemas.Schemas (ns);
265 if (colln.Count > 0) {
266 if (colln.Count > 1)
267 throw new Exception (String.Format (
268 "More than 1 schema for namespace '{0}' found.", ns));
269 foreach (object o in colln)
270 //return colln [0]
271 return (o as XmlSchema);
274 XmlSchema schema = new XmlSchema ();
275 schema.TargetNamespace = ns;
276 schema.ElementFormDefault = XmlSchemaForm.Qualified;
277 schemas.Add (schema);
279 return schema;
282 protected XmlQualifiedName GetQualifiedName (Type type)
284 if (qname_table.ContainsKey (type))
285 return qname_table [type];
287 QName qname = KnownTypes.GetQName (type);
288 if (qname.Namespace == KnownTypeCollection.MSSimpleNamespace)
289 qname = new QName (qname.Name, XmlSchema.Namespace);
291 qname_table [type] = qname;
292 return qname;
294 #endif
296 public virtual void Serialize (object graph,
297 XmlFormatterSerializer serializer)
299 string label = null;
300 if (IsReference) {
301 label = (string) serializer.References [graph];
302 if (label != null) {
303 serializer.Writer.WriteAttributeString ("z", "Ref", KnownTypeCollection.MSSimpleNamespace, label);
304 return;
306 label = "i" + (serializer.References.Count + 1);
307 serializer.References.Add (graph, label);
309 else if (serializer.SerializingObjects.Contains (graph))
310 throw new SerializationException (String.Format ("Circular reference of an object in the object graph was found: '{0}' of type {1}", graph, graph.GetType ()));
311 serializer.SerializingObjects.Add (graph);
313 if (label != null)
314 serializer.Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
316 SerializeNonReference (graph, serializer);
318 serializer.SerializingObjects.Remove (graph);
321 public virtual void SerializeNonReference (object graph,
322 XmlFormatterSerializer serializer)
324 foreach (DataMemberInfo dmi in Members) {
325 FieldInfo fi = dmi.Member as FieldInfo;
326 PropertyInfo pi = fi == null ?
327 (PropertyInfo) dmi.Member : null;
328 Type type = fi != null ?
329 fi.FieldType : pi.PropertyType;
330 object value = fi != null ?
331 fi.GetValue (graph) :
332 pi.GetValue (graph, null);
334 serializer.WriteStartElement (dmi.XmlName, dmi.XmlRootNamespace, dmi.XmlNamespace);
335 serializer.Serialize (type, value);
336 serializer.WriteEndElement ();
340 public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
342 bool isEmpty = reader.IsEmptyElement;
343 reader.ReadStartElement ();
344 reader.MoveToContent ();
346 object res;
348 if (isEmpty)
349 res = DeserializeEmptyContent (reader, deserializer);
350 else
351 res = DeserializeContent (reader, deserializer);
353 reader.MoveToContent ();
354 if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
355 reader.ReadEndElement ();
356 else if (!isEmpty && reader.NodeType != XmlNodeType.None) {
357 var li = reader as IXmlLineInfo;
358 throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.{4}",
359 reader.NodeType,
360 reader.Name,
361 reader.NamespaceURI,
362 RuntimeType.FullName,
363 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : String.Empty));
365 return res;
368 // This is sort of hack. The argument reader already moved ahead of
369 // the actual empty element.It's just for historical consistency.
370 public virtual object DeserializeEmptyContent (XmlReader reader,
371 XmlFormatterDeserializer deserializer)
373 return DeserializeContent (reader, deserializer, true);
376 public virtual object DeserializeContent (XmlReader reader,
377 XmlFormatterDeserializer deserializer)
379 return DeserializeContent (reader, deserializer, false);
382 object DeserializeContent (XmlReader reader,
383 XmlFormatterDeserializer deserializer, bool empty)
385 object instance = FormatterServices.GetUninitializedObject (RuntimeType);
386 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
387 bool [] filled = new bool [Members.Count];
388 int memberInd = -1, ordered = -1;
389 while (!empty && reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
390 DataMemberInfo dmi = null;
391 int i = 0;
392 for (; i < Members.Count; i++) { // unordered
393 if (Members [i].Order >= 0)
394 break;
395 if (reader.LocalName == Members [i].XmlName &&
396 reader.NamespaceURI == Members [i].XmlRootNamespace) {
397 memberInd = i;
398 dmi = Members [i];
399 break;
402 for (i = Math.Max (i, ordered); i < Members.Count; i++) { // ordered
403 if (dmi != null)
404 break;
405 if (reader.LocalName == Members [i].XmlName &&
406 reader.NamespaceURI == Members [i].XmlRootNamespace) {
407 memberInd = i;
408 ordered = i;
409 dmi = Members [i];
410 break;
414 if (dmi == null) {
415 reader.Skip ();
416 continue;
418 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
419 filled [memberInd] = true;
420 reader.MoveToContent ();
422 for (int i = 0; i < Members.Count; i++)
423 if (!filled [i] && Members [i].IsRequired)
424 throw MissingRequiredMember (Members [i], reader);
426 return instance;
429 // For now it could be private.
430 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
432 var li = reader as IXmlLineInfo;
433 return new ArgumentException (String.Format ("Data contract member {0} for the type {1} is required, but missing in the input XML.{2}",
434 new QName (dmi.XmlName, dmi.XmlNamespace),
435 RuntimeType,
436 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : null));
439 // For now it could be private.
440 protected void SetValue (DataMemberInfo dmi, object obj, object value)
442 try {
443 if (dmi.Member is PropertyInfo)
444 ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
445 else
446 ((FieldInfo) dmi.Member).SetValue (obj, value);
447 } catch (Exception ex) {
448 throw new InvalidOperationException (String.Format ("Failed to set value of type {0} for property {1}", value != null ? value.GetType () : null, dmi.Member), ex);
452 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type type)
454 KnownTypes.Add (type);
455 QName qname = KnownTypes.GetQName (type);
456 string rootNamespace = KnownTypes.GetQName (mi.DeclaringType).Namespace;
457 if (KnownTypeCollection.GetPrimitiveTypeFromName (qname.Name) != null)
458 return new DataMemberInfo (mi, dma, rootNamespace, null);
459 else
460 return new DataMemberInfo (mi, dma, rootNamespace, qname.Namespace);
464 internal partial class XmlSerializableMap : SerializationMap
466 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
467 : base (type, qname, knownTypes)
471 public override void Serialize (object graph, XmlFormatterSerializer serializer)
473 IXmlSerializable ixs = graph as IXmlSerializable;
474 if (ixs == null)
475 //FIXME: Throw what exception here?
476 throw new SerializationException ();
478 ixs.WriteXml (serializer.Writer);
481 public override object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
483 IXmlSerializable ixs = (IXmlSerializable) FormatterServices.GetUninitializedObject (RuntimeType);
484 ixs.ReadXml (reader);
485 return ixs;
488 #if !NET_2_1
489 // FIXME: verify return value sanity.
490 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
492 return null;
494 #endif
497 internal partial class SharedContractMap : SerializationMap
499 public SharedContractMap (
500 Type type, QName qname, KnownTypeCollection knownTypes)
501 : base (type, qname, knownTypes)
505 internal void Initialize ()
507 Type baseType = RuntimeType;
508 List <DataMemberInfo> members = new List <DataMemberInfo> ();
509 object [] atts = baseType.GetCustomAttributes (
510 typeof (DataContractAttribute), false);
511 IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
513 while (baseType != null) {
514 QName bqname = KnownTypes.GetQName (baseType);
516 members = GetMembers (baseType, bqname, true);
517 members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
518 Members.InsertRange (0, members);
519 members.Clear ();
521 baseType = baseType.BaseType;
525 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
527 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
528 BindingFlags flags = AllInstanceFlags;
529 if (declared_only)
530 flags |= BindingFlags.DeclaredOnly;
532 foreach (PropertyInfo pi in type.GetProperties (flags)) {
533 DataMemberAttribute dma =
534 GetDataMemberAttribute (pi);
535 if (dma == null)
536 continue;
537 KnownTypes.TryRegister (pi.PropertyType);
538 var map = KnownTypes.FindUserMap (pi.PropertyType);
539 if (!pi.CanRead || (!pi.CanWrite && !(map is ICollectionTypeMap)))
540 throw new InvalidDataContractException (String.Format (
541 "DataMember property '{0}' on type '{1}' must have both getter and setter.", pi, pi.DeclaringType));
542 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType));
545 foreach (FieldInfo fi in type.GetFields (flags)) {
546 DataMemberAttribute dma =
547 GetDataMemberAttribute (fi);
548 if (dma == null)
549 continue;
550 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
553 return data_members;
556 public override List<DataMemberInfo> GetMembers ()
558 return Members;
562 internal partial class DefaultTypeMap : SerializationMap
564 public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
565 : base (type, KnownTypeCollection.GetStaticQName (type), knownTypes)
567 Members.AddRange (GetDefaultMembers ());
570 List<DataMemberInfo> GetDefaultMembers ()
572 var l = new List<DataMemberInfo> ();
573 foreach (var mi in RuntimeType.GetMembers ()) {
574 Type mt = null;
575 FieldInfo fi = mi as FieldInfo;
576 mt = fi == null ? null : fi.FieldType;
577 PropertyInfo pi = mi as PropertyInfo;
578 if (pi != null && pi.CanRead && pi.CanWrite && pi.GetIndexParameters ().Length == 0)
579 mt = pi.PropertyType;
580 if (mt == null)
581 continue;
582 if (mi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length != 0)
583 continue;
584 l.Add (new DataMemberInfo (mi, new DataMemberAttribute (), null, null));
586 l.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
587 return l;
591 // FIXME: it still needs to consider ItemName/KeyName/ValueName
592 // (especially Dictionary collection is not likely considered yet.)
593 internal partial class CollectionContractTypeMap : CollectionTypeMap
595 CollectionDataContractAttribute a;
597 public CollectionContractTypeMap (
598 Type type, CollectionDataContractAttribute a, Type elementType,
599 QName qname, KnownTypeCollection knownTypes)
600 : base (type, elementType, qname, knownTypes)
602 this.a = a;
603 IsReference = a.IsReference;
606 internal override string CurrentNamespace {
607 get { return XmlName.Namespace; }
611 internal interface ICollectionTypeMap
615 internal partial class CollectionTypeMap : SerializationMap, ICollectionTypeMap
617 Type element_type;
618 internal QName element_qname;
619 MethodInfo add_method;
621 public CollectionTypeMap (
622 Type type, Type elementType,
623 QName qname, KnownTypeCollection knownTypes)
624 : base (type, qname, knownTypes)
626 element_type = elementType;
627 element_qname = KnownTypes.GetQName (element_type);
628 var icoll = GetGenericCollectionInterface (RuntimeType);
629 if (icoll != null) {
630 if (RuntimeType.IsInterface) {
631 add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
632 } else {
633 var imap = RuntimeType.GetInterfaceMap (icoll);
634 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
635 if (imap.InterfaceMethods [i].Name == "Add") {
636 add_method = imap.TargetMethods [i];
637 break;
639 if (add_method == null)
640 add_method = type.GetMethod ("Add", icoll.GetGenericArguments ());
645 static Type GetGenericCollectionInterface (Type type)
647 foreach (var iface in type.GetInterfaces ())
648 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (ICollection<>))
649 return iface;
651 return null;
654 public override bool OutputXsiType {
655 get { return false; }
658 internal virtual string CurrentNamespace {
659 get {
660 string ns = element_qname.Namespace;
661 if (ns == KnownTypeCollection.MSSimpleNamespace)
662 ns = KnownTypeCollection.MSArraysNamespace;
663 return ns;
667 public override void SerializeNonReference (object graph,
668 XmlFormatterSerializer serializer)
671 foreach (object o in (IEnumerable) graph) {
672 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, CurrentNamespace);
673 serializer.Serialize (element_type, o);
674 serializer.WriteEndElement ();
678 object CreateInstance ()
680 if (RuntimeType.IsArray)
681 return new ArrayList ();
682 if (RuntimeType.IsInterface) {
683 var icoll = GetGenericCollectionInterface (RuntimeType);
684 if (icoll != null)
685 return Activator.CreateInstance (typeof (List<>).MakeGenericType (RuntimeType.GetGenericArguments () [0])); // List<T>
686 else // non-generic
687 return new ArrayList ();
689 #if NET_2_1 // FIXME: is it fine?
690 return Activator.CreateInstance (RuntimeType);
691 #else
692 return Activator.CreateInstance (RuntimeType, true);
693 #endif
696 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
698 var instance = CreateInstance ();
699 if (RuntimeType.IsArray)
700 return ((ArrayList)instance).ToArray (element_type);
701 else
702 return instance;
705 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
707 object instance = CreateInstance ();
708 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
709 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
710 object elem = deserializer.Deserialize (element_type, reader);
711 if (instance is IList)
712 ((IList)instance).Add (elem);
713 else if (add_method != null)
714 add_method.Invoke (instance, new object [] {elem});
715 else
716 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
717 reader.MoveToContent ();
719 if (RuntimeType.IsArray)
720 return ((ArrayList)instance).ToArray (element_type);
721 return instance;
724 public override List<DataMemberInfo> GetMembers ()
726 //Shouldn't come here at all!
727 throw new NotImplementedException ();
730 #if !NET_2_1
731 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
733 if (generated_schema_types.ContainsKey (XmlName))
734 return null;
736 if (generated_schema_types.ContainsKey (XmlName))
737 return generated_schema_types [XmlName];
739 QName element_qname = GetQualifiedName (element_type);
741 XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
742 complex_type.Name = XmlName.Name;
744 XmlSchemaSequence sequence = new XmlSchemaSequence ();
745 XmlSchemaElement element = new XmlSchemaElement ();
747 element.MinOccurs = 0;
748 element.MaxOccursString = "unbounded";
749 element.Name = element_qname.Name;
751 KnownTypes.Add (element_type);
752 SerializationMap map = KnownTypes.FindUserMap (element_type);
753 if (map != null) {// non-primitive type
754 map.GetSchemaType (schemas, generated_schema_types);
755 element.IsNillable = true;
758 element.SchemaTypeName = element_qname;
760 sequence.Items.Add (element);
761 complex_type.Particle = sequence;
763 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
764 schema.Items.Add (complex_type);
765 schema.Items.Add (GetSchemaElement (XmlName, complex_type));
766 schemas.Reprocess (schema);
768 generated_schema_types [XmlName] = complex_type;
770 return complex_type;
772 #endif
775 internal partial class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
777 Type key_type, value_type;
778 QName dict_qname, item_qname, key_qname, value_qname;
779 MethodInfo add_method;
780 CollectionDataContractAttribute a;
782 public DictionaryTypeMap (
783 Type type, CollectionDataContractAttribute a, KnownTypeCollection knownTypes)
784 : base (type, QName.Empty, knownTypes)
786 this.a = a;
788 key_type = typeof (object);
789 value_type = typeof (object);
791 var idic = GetGenericDictionaryInterface (RuntimeType);
792 if (idic != null) {
793 var imap = RuntimeType.GetInterfaceMap (idic);
794 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
795 if (imap.InterfaceMethods [i].Name == "Add") {
796 add_method = imap.TargetMethods [i];
797 break;
799 var argtypes = idic.GetGenericArguments();
800 key_type = argtypes [0];
801 value_type = argtypes [1];
802 if (add_method == null)
803 add_method = type.GetMethod ("Add", argtypes);
806 XmlName = GetDictionaryQName ();
807 item_qname = GetItemQName ();
808 key_qname = GetKeyQName ();
809 value_qname = GetValueQName ();
812 static Type GetGenericDictionaryInterface (Type type)
814 foreach (var iface in type.GetInterfaces ())
815 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>))
816 return iface;
818 return null;
821 string ContractNamespace {
822 get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
825 public Type KeyType { get { return key_type; } }
826 public Type ValueType { get { return value_type; } }
828 static readonly QName kvpair_key_qname = new QName ("Key", KnownTypeCollection.MSArraysNamespace);
829 static readonly QName kvpair_value_qname = new QName ("Value", KnownTypeCollection.MSArraysNamespace);
831 internal virtual QName GetDictionaryQName ()
833 if (a != null && !String.IsNullOrEmpty (a.Name))
834 return new QName (a.Name, ContractNamespace);
835 return new QName ("ArrayOf" + GetItemQName ().Name, KnownTypeCollection.MSArraysNamespace);
838 internal virtual QName GetItemQName ()
840 if (a != null && !String.IsNullOrEmpty (a.ItemName))
841 return new QName (a.ItemName, ContractNamespace);
842 return new QName ("KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name, KnownTypeCollection.MSArraysNamespace);
845 internal virtual QName GetKeyQName ()
847 if (a != null && !String.IsNullOrEmpty (a.KeyName))
848 return new QName (a.KeyName, ContractNamespace);
849 return kvpair_key_qname;
852 internal virtual QName GetValueQName ()
854 if (a != null && !String.IsNullOrEmpty (a.ValueName))
855 return new QName (a.ValueName, ContractNamespace);
856 return kvpair_value_qname;
859 internal virtual string CurrentNamespace {
860 get {
861 string ns = item_qname.Namespace;
862 if (ns == KnownTypeCollection.MSSimpleNamespace)
863 ns = KnownTypeCollection.MSArraysNamespace;
864 return ns;
868 Type pair_type;
869 PropertyInfo pair_key_property, pair_value_property;
871 public override void SerializeNonReference (object graph,
872 XmlFormatterSerializer serializer)
874 if (add_method != null) { // generic
875 if (pair_type == null) {
876 pair_type = typeof (KeyValuePair<,>).MakeGenericType (add_method.DeclaringType.GetGenericArguments ());
877 pair_key_property = pair_type.GetProperty ("Key");
878 pair_value_property = pair_type.GetProperty ("Value");
880 foreach (object p in (IEnumerable) graph) {
881 serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
882 serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
883 serializer.Serialize (pair_key_property.PropertyType, pair_key_property.GetValue (p, null));
884 serializer.WriteEndElement ();
885 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
886 serializer.Serialize (pair_value_property.PropertyType, pair_value_property.GetValue (p, null));
887 serializer.WriteEndElement ();
888 serializer.WriteEndElement ();
890 } else { // non-generic
891 foreach (DictionaryEntry p in (IEnumerable) graph) {
892 serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
893 serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
894 serializer.Serialize (key_type, p.Key);
895 serializer.WriteEndElement ();
896 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
897 serializer.Serialize (value_type, p.Value);
898 serializer.WriteEndElement ();
899 serializer.WriteEndElement ();
904 object CreateInstance ()
906 if (RuntimeType.IsInterface) {
907 if (RuntimeType.IsGenericType && Array.IndexOf (RuntimeType.GetGenericTypeDefinition ().GetInterfaces (), typeof (IDictionary<,>)) >= 0) {
908 var gargs = RuntimeType.GetGenericArguments ();
909 return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (gargs [0], gargs [1])); // Dictionary<T>
911 else // non-generic
912 return new Hashtable ();
914 #if NET_2_1 // FIXME: is it fine?
915 return Activator.CreateInstance (RuntimeType);
916 #else
917 return Activator.CreateInstance (RuntimeType, true);
918 #endif
921 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
923 return DeserializeContent (reader, deserializer);
926 public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
928 object instance = CreateInstance ();
929 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
930 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
931 if (reader.IsEmptyElement)
932 throw new XmlException (String.Format ("Unexpected empty element for dictionary entry: name {0}", reader.Name));
933 // FIXME: sloppy parsing
934 reader.ReadStartElement ();// item_qname.Name, item_qname.Namespace);
935 reader.MoveToContent ();
936 object key = deserializer.Deserialize (key_type, reader);
937 reader.MoveToContent ();
938 object val = deserializer.Deserialize (value_type, reader);
939 reader.ReadEndElement (); // of pair
941 if (instance is IDictionary)
942 ((IDictionary)instance).Add (key, val);
943 else if (add_method != null)
944 add_method.Invoke (instance, new object [] {key, val});
945 else
946 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
948 return instance;
951 public override List<DataMemberInfo> GetMembers ()
953 //Shouldn't come here at all!
954 throw new NotImplementedException ();
957 #if !NET_2_1
958 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
960 throw new NotImplementedException ();
962 #endif
965 internal partial class SharedTypeMap : SerializationMap
967 public SharedTypeMap (
968 Type type, QName qname, KnownTypeCollection knownTypes)
969 : base (type, qname, knownTypes)
973 public void Initialize ()
975 Members = GetMembers (RuntimeType, XmlName, false);
978 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
980 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
981 BindingFlags flags = AllInstanceFlags;
982 if (declared_only)
983 flags |= BindingFlags.DeclaredOnly;
985 foreach (FieldInfo fi in type.GetFields (flags)) {
986 if (fi.GetCustomAttributes (
987 typeof (NonSerializedAttribute),
988 false).Length > 0)
989 continue;
991 if (fi.IsInitOnly)
992 throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
993 DataMemberAttribute dma = new DataMemberAttribute ();
994 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
997 data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance); // alphabetic order.
999 return data_members;
1002 // Does this make sense? I doubt.
1003 public override List<DataMemberInfo> GetMembers ()
1005 return Members;
1006 //return GetMembers (RuntimeType, XmlName, true);
1010 internal partial class EnumMap : SerializationMap
1012 List<EnumMemberInfo> enum_members;
1013 bool flag_attr;
1015 public EnumMap (
1016 Type type, QName qname, KnownTypeCollection knownTypes)
1017 : base (type, qname, knownTypes)
1019 bool has_dc = false;
1020 object [] atts = RuntimeType.GetCustomAttributes (
1021 typeof (DataContractAttribute), false);
1022 if (atts.Length != 0)
1023 has_dc = true;
1024 flag_attr = type.GetCustomAttributes (typeof (FlagsAttribute), false).Length > 0;
1026 enum_members = new List<EnumMemberInfo> ();
1027 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
1029 foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
1030 string name = fi.Name;
1031 if (has_dc) {
1032 EnumMemberAttribute ema =
1033 GetEnumMemberAttribute (fi);
1034 if (ema == null)
1035 continue;
1037 if (ema.Value != null)
1038 name = ema.Value;
1041 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
1045 private EnumMemberAttribute GetEnumMemberAttribute (
1046 MemberInfo mi)
1048 object [] atts = mi.GetCustomAttributes (
1049 typeof (EnumMemberAttribute), false);
1050 if (atts.Length == 0)
1051 return null;
1052 return (EnumMemberAttribute) atts [0];
1055 #if !NET_2_1
1056 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
1058 if (generated_schema_types.ContainsKey (XmlName))
1059 return generated_schema_types [XmlName];
1061 XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType ();
1062 simpleType.Name = XmlName.Name;
1064 XmlSchemaSimpleTypeRestriction simpleRestriction = new XmlSchemaSimpleTypeRestriction ();
1065 simpleType.Content = simpleRestriction;
1066 simpleRestriction.BaseTypeName = new XmlQualifiedName ("string", XmlSchema.Namespace);
1068 foreach (EnumMemberInfo emi in enum_members) {
1069 XmlSchemaEnumerationFacet e = new XmlSchemaEnumerationFacet ();
1070 e.Value = emi.XmlName;
1071 simpleRestriction.Facets.Add (e);
1074 generated_schema_types [XmlName] = simpleType;
1076 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
1077 XmlSchemaElement element = GetSchemaElement (XmlName, simpleType);
1078 element.IsNillable = true;
1080 schema.Items.Add (simpleType);
1081 schema.Items.Add (element);
1083 return simpleType;
1085 #endif
1087 public override void Serialize (object graph,
1088 XmlFormatterSerializer serializer)
1090 foreach (EnumMemberInfo emi in enum_members) {
1091 if (Enum.Equals (emi.Value, graph)) {
1092 serializer.Writer.WriteString (emi.XmlName);
1093 return;
1097 throw new SerializationException (String.Format (
1098 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
1101 public override object DeserializeEmptyContent (XmlReader reader,
1102 XmlFormatterDeserializer deserializer)
1104 if (!flag_attr)
1105 throw new SerializationException (String.Format ("Enum value '' is invalid for type '{0}' and cannot be deserialized.", RuntimeType));
1106 return Enum.ToObject (RuntimeType, 0);
1109 public override object DeserializeContent (XmlReader reader,
1110 XmlFormatterDeserializer deserializer)
1112 string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
1114 if (value != String.Empty) {
1115 foreach (EnumMemberInfo emi in enum_members)
1116 if (emi.XmlName == value)
1117 return emi.Value;
1120 if (!flag_attr)
1121 throw new SerializationException (String.Format ("Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
1122 return Enum.ToObject (RuntimeType, 0);
1126 internal struct EnumMemberInfo
1128 public readonly string XmlName;
1129 public readonly object Value;
1131 public EnumMemberInfo (string name, object value)
1133 XmlName = name;
1134 Value = value;
1138 internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
1140 public readonly int Order;
1141 public readonly bool IsRequired;
1142 public readonly string XmlName;
1143 public readonly MemberInfo Member;
1144 public readonly string XmlNamespace;
1145 public readonly string XmlRootNamespace;
1146 public readonly Type MemberType;
1148 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
1150 if (dma == null)
1151 throw new ArgumentNullException ("dma");
1152 Order = dma.Order;
1153 Member = member;
1154 IsRequired = dma.IsRequired;
1155 XmlName = dma.Name != null ? dma.Name : member.Name;
1156 XmlNamespace = ns;
1157 XmlRootNamespace = rootNamespce;
1158 if (Member is FieldInfo)
1159 MemberType = ((FieldInfo) Member).FieldType;
1160 else
1161 MemberType = ((PropertyInfo) Member).PropertyType;
1164 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
1165 , IComparer // see bug #76361
1167 public static readonly DataMemberInfoComparer Instance
1168 = new DataMemberInfoComparer ();
1170 private DataMemberInfoComparer () {}
1172 public int Compare (object o1, object o2)
1174 return Compare ((DataMemberInfo) o1,
1175 (DataMemberInfo) o2);
1178 public int Compare (DataMemberInfo d1, DataMemberInfo d2)
1180 if (d1.Order == d2.Order)
1181 return String.CompareOrdinal (d1.XmlName, d2.XmlName);
1183 return d1.Order - d2.Order;
1188 #endif