5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Ankit Jain <JAnkit@novell.com>
7 // Duncan Mak (duncan@ximian.com)
8 // Eyal Alaluf (eyala@mainsoft.com)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.ObjectModel;
38 using System.Reflection;
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:
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/" .
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).
67 - For type A to be serializable, the base type B of A must be
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
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 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;
92 XmlSchemaSet schema_set;
96 Dictionary<Type, QName> qname_table = new Dictionary<Type, QName> ();
98 protected SerializationMap (
99 Type type, QName qname, KnownTypeCollection knownTypes)
101 KnownTypes = knownTypes;
103 if (qname.Namespace == String.Empty)
104 qname = new QName (qname.Name,
105 "http://schemas.datacontract.org/2004/07/" + type.Namespace);
108 Members = new List<DataMemberInfo> ();
111 public virtual bool OutputXsiType {
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 (
127 object [] atts = mi.GetCustomAttributes (
128 typeof (DataMemberAttribute), false);
129 if (atts.Length == 0)
131 return (DataMemberAttribute) atts [0];
134 bool IsPrimitive (Type type)
136 return (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object));
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))
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);
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);
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))
196 XmlSchemaElement element = new XmlSchemaElement ();
197 element.Name = dmi.XmlName;
199 KnownTypes.Add (dmi.MemberType);
200 SerializationMap map = KnownTypes.FindUserMap (dmi.MemberType);
202 XmlSchemaType schema_type = map.GetSchemaType (schemas, generated_schema_types);
203 if (schema_type is XmlSchemaComplexType)
204 element.IsNillable = true;
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);
223 //FIXME: Replace with a dictionary ?
224 void AddImport (XmlSchema schema, string ns)
226 if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
229 foreach (XmlSchemaObject o in schema.Includes) {
230 XmlSchemaImport import = o as XmlSchemaImport;
233 if (import.Namespace == ns)
237 XmlSchemaImport imp = new XmlSchemaImport ();
239 schema.Includes.Add (imp);
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));
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) {
267 throw new Exception (String.Format (
268 "More than 1 schema for namespace '{0}' found.", ns));
269 foreach (object o in colln)
271 return (o as XmlSchema);
274 XmlSchema schema = new XmlSchema ();
275 schema.TargetNamespace = ns;
276 schema.ElementFormDefault = XmlSchemaForm.Qualified;
277 schemas.Add (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;
296 public virtual void Serialize (object graph,
297 XmlFormatterSerializer serializer)
301 label = (string) serializer.References [graph];
303 serializer.Writer.WriteAttributeString ("z", "Ref", KnownTypeCollection.MSSimpleNamespace, label);
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);
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 /* Deserialize non-primitive types */
342 // This is sort of hack. The argument reader already moved ahead of
343 // the actual empty element.It's just for historical consistency.
344 public virtual object DeserializeEmptyContent (XmlReader reader,
345 XmlFormatterDeserializer deserializer)
347 return DeserializeContent (reader, deserializer);
350 public virtual object DeserializeContent (XmlReader reader,
351 XmlFormatterDeserializer deserializer)
353 object instance = FormatterServices.GetUninitializedObject (RuntimeType);
354 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
355 bool [] filled = new bool [Members.Count];
357 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
358 DataMemberInfo dmi = null;
360 for (; i < Members.Count; i++) { // unordered
361 if (Members [i].Order >= 0)
363 if (reader.LocalName == Members [i].XmlName &&
364 reader.NamespaceURI == Members [i].XmlRootNamespace) {
370 for (; i < Members.Count; i++) { // ordered
373 if (reader.LocalName == Members [i].XmlName &&
374 reader.NamespaceURI == Members [i].XmlRootNamespace) {
385 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
386 filled [memberInd] = true;
388 for (int i = 0; i < Members.Count; i++)
389 if (!filled [i] && Members [i].IsRequired)
390 throw MissingRequiredMember (Members [i], reader);
395 // For now it could be private.
396 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
398 return new ArgumentException (String.Format ("Data contract member {0} is required, but missing in the input XML.", new QName (dmi.XmlName, dmi.XmlNamespace)));
401 // For now it could be private.
402 protected void SetValue (DataMemberInfo dmi, object obj, object value)
404 if (dmi.Member is PropertyInfo)
405 ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
407 ((FieldInfo) dmi.Member).SetValue (obj, value);
410 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type type)
412 KnownTypes.Add (type);
413 QName qname = KnownTypes.GetQName (type);
414 string rootNamespace = KnownTypes.GetQName (mi.DeclaringType).Namespace;
415 if (KnownTypeCollection.GetPrimitiveTypeFromName (qname.Name) != null)
416 return new DataMemberInfo (mi, dma, rootNamespace, null);
418 return new DataMemberInfo (mi, dma, rootNamespace, qname.Namespace);
422 internal class XmlSerializableMap : SerializationMap
424 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
425 : base (type, qname, knownTypes)
429 public override void Serialize (object graph, XmlFormatterSerializer serializer)
431 IXmlSerializable ixs = graph as IXmlSerializable;
433 //FIXME: Throw what exception here?
434 throw new SerializationException ();
436 ixs.WriteXml (serializer.Writer);
440 // FIXME: verify return value sanity.
441 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
448 internal class SharedContractMap : SerializationMap
450 public SharedContractMap (
451 Type type, QName qname, KnownTypeCollection knownTypes)
452 : base (type, qname, knownTypes)
454 Type baseType = type;
455 List <DataMemberInfo> members = new List <DataMemberInfo> ();
456 object [] atts = type.GetCustomAttributes (
457 typeof (DataContractAttribute), false);
458 IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
460 while (baseType != null) {
461 QName bqname = knownTypes.GetQName (baseType);
463 members = GetMembers (baseType, bqname, true);
464 Members.InsertRange (0, members);
467 baseType = baseType.BaseType;
470 // Members.Sort (delegate (
471 // DataMemberInfo d1, DataMemberInfo d2) {
472 // return d1.Order - d2.Order;
476 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
478 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
479 BindingFlags flags = AllInstanceFlags;
481 flags |= BindingFlags.DeclaredOnly;
483 foreach (PropertyInfo pi in type.GetProperties (flags)) {
484 DataMemberAttribute dma =
485 GetDataMemberAttribute (pi);
488 KnownTypes.TryRegister (pi.PropertyType);
489 var map = KnownTypes.FindUserMap (pi.PropertyType);
490 if (!pi.CanRead || (!pi.CanWrite && !(map is ICollectionTypeMap)))
491 throw new InvalidDataContractException (String.Format (
492 "DataMember property '{0}' on type '{1}' must have both getter and setter.", pi, pi.DeclaringType));
493 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType));
496 foreach (FieldInfo fi in type.GetFields (flags)) {
497 DataMemberAttribute dma =
498 GetDataMemberAttribute (fi);
502 throw new InvalidDataContractException (String.Format (
503 "DataMember field {0} must not be read-only.", fi));
504 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
507 data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
512 public override List<DataMemberInfo> GetMembers ()
514 return GetMembers (RuntimeType, XmlName, true);
518 internal class DefaultTypeMap : SerializationMap
520 public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
521 : base (type, KnownTypeCollection.GetContractQName (type, null, null), knownTypes)
523 Members.AddRange (GetDefaultMembers ());
526 List<DataMemberInfo> GetDefaultMembers ()
528 var l = new List<DataMemberInfo> ();
529 foreach (var mi in RuntimeType.GetMembers ()) {
531 FieldInfo fi = mi as FieldInfo;
532 mt = fi == null ? null : fi.FieldType;
533 PropertyInfo pi = mi as PropertyInfo;
534 if (pi != null && pi.CanRead && pi.CanWrite && pi.GetIndexParameters ().Length == 0)
535 mt = pi.PropertyType;
538 l.Add (new DataMemberInfo (mi, new DataMemberAttribute (), null, null));
544 // FIXME: it still needs to consider ItemName/KeyName/ValueName
545 // (especially Dictionary collection is not likely considered yet.)
546 internal class CollectionContractTypeMap : CollectionTypeMap
548 public CollectionContractTypeMap (
549 Type type, CollectionDataContractAttribute a, Type elementType,
550 QName qname, KnownTypeCollection knownTypes)
551 : base (type, elementType, qname, knownTypes)
553 IsReference = a.IsReference;
556 internal override string CurrentNamespace {
557 get { return XmlName.Namespace; }
561 internal interface ICollectionTypeMap
565 internal class CollectionTypeMap : SerializationMap, ICollectionTypeMap
568 internal QName element_qname;
569 MethodInfo add_method;
571 public CollectionTypeMap (
572 Type type, Type elementType,
573 QName qname, KnownTypeCollection knownTypes)
574 : base (type, qname, knownTypes)
576 element_type = elementType;
577 element_qname = KnownTypes.GetQName (element_type);
578 var icoll = RuntimeType.GetInterfaces ().FirstOrDefault (
579 iface => iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (ICollection<>));
581 if (RuntimeType.IsInterface) {
582 add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
584 var imap = RuntimeType.GetInterfaceMap (icoll);
585 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
586 if (imap.InterfaceMethods [i].Name == "Add") {
587 add_method = imap.TargetMethods [i];
590 if (add_method == null)
591 add_method = type.GetMethod ("Add", icoll.GetGenericArguments ());
596 public override bool OutputXsiType {
597 get { return false; }
600 internal virtual string CurrentNamespace {
602 string ns = element_qname.Namespace;
603 if (ns == KnownTypeCollection.MSSimpleNamespace)
604 ns = KnownTypeCollection.MSArraysNamespace;
609 public override void SerializeNonReference (object graph,
610 XmlFormatterSerializer serializer)
613 foreach (object o in (IEnumerable) graph) {
614 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, CurrentNamespace);
615 serializer.Serialize (element_type, o);
616 serializer.WriteEndElement ();
620 object CreateInstance ()
622 if (RuntimeType.IsArray)
623 return new ArrayList ();
625 #if NET_2_1 // FIXME: is it fine?
626 return Activator.CreateInstance (RuntimeType);
628 return Activator.CreateInstance (RuntimeType, true);
632 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
634 return CreateInstance ();
637 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
639 object instance = CreateInstance ();
640 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
641 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
642 object elem = deserializer.Deserialize (element_type, reader);
643 if (instance is IList)
644 ((IList)instance).Add (elem);
645 else if (add_method != null)
646 add_method.Invoke (instance, new object [] {elem});
648 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
650 if (RuntimeType.IsArray)
651 return ((ArrayList)instance).ToArray (element_type);
655 public override List<DataMemberInfo> GetMembers ()
657 //Shouldn't come here at all!
658 throw new NotImplementedException ();
662 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
664 if (generated_schema_types.ContainsKey (XmlName))
667 if (generated_schema_types.ContainsKey (XmlName))
668 return generated_schema_types [XmlName];
670 QName element_qname = GetQualifiedName (element_type);
672 XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
673 complex_type.Name = XmlName.Name;
675 XmlSchemaSequence sequence = new XmlSchemaSequence ();
676 XmlSchemaElement element = new XmlSchemaElement ();
678 element.MinOccurs = 0;
679 element.MaxOccursString = "unbounded";
680 element.Name = element_qname.Name;
682 KnownTypes.Add (element_type);
683 SerializationMap map = KnownTypes.FindUserMap (element_type);
684 if (map != null) {// non-primitive type
685 map.GetSchemaType (schemas, generated_schema_types);
686 element.IsNillable = true;
689 element.SchemaTypeName = element_qname;
691 sequence.Items.Add (element);
692 complex_type.Particle = sequence;
694 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
695 schema.Items.Add (complex_type);
696 schema.Items.Add (GetSchemaElement (XmlName, complex_type));
697 schemas.Reprocess (schema);
699 generated_schema_types [XmlName] = complex_type;
706 internal class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
708 Type key_type, value_type;
709 QName dict_qname, item_qname, key_qname, value_qname;
710 MethodInfo add_method;
711 CollectionDataContractAttribute a;
713 public DictionaryTypeMap (
714 Type type, CollectionDataContractAttribute a, KnownTypeCollection knownTypes)
715 : base (type, QName.Empty, knownTypes)
719 key_type = typeof (object);
720 value_type = typeof (object);
722 var idic = RuntimeType.GetInterfaces ().FirstOrDefault (
723 iface => iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>));
725 var imap = RuntimeType.GetInterfaceMap (idic);
726 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
727 if (imap.InterfaceMethods [i].Name == "Add") {
728 add_method = imap.TargetMethods [i];
731 var argtypes = idic.GetGenericArguments();
732 key_type = argtypes [0];
733 value_type = argtypes [1];
734 if (add_method == null)
735 add_method = type.GetMethod ("Add", argtypes);
738 XmlName = GetDictionaryQName ();
739 item_qname = GetItemQName ();
740 key_qname = GetKeyQName ();
741 value_qname = GetValueQName ();
744 string ContractNamespace {
745 get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
748 public Type KeyType { get { return key_type; } }
749 public Type ValueType { get { return value_type; } }
751 static readonly QName kvpair_key_qname = new QName ("Key", KnownTypeCollection.MSArraysNamespace);
752 static readonly QName kvpair_value_qname = new QName ("Value", KnownTypeCollection.MSArraysNamespace);
754 internal virtual QName GetDictionaryQName ()
756 if (a != null && !String.IsNullOrEmpty (a.Name))
757 return new QName (a.Name, ContractNamespace);
758 return new QName ("ArrayOf" + GetItemQName ().Name, KnownTypeCollection.MSArraysNamespace);
761 internal virtual QName GetItemQName ()
763 if (a != null && !String.IsNullOrEmpty (a.ItemName))
764 return new QName (a.ItemName, ContractNamespace);
765 return new QName ("KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name, KnownTypeCollection.MSArraysNamespace);
768 internal virtual QName GetKeyQName ()
770 if (a != null && !String.IsNullOrEmpty (a.KeyName))
771 return new QName (a.KeyName, ContractNamespace);
772 return kvpair_key_qname;
775 internal virtual QName GetValueQName ()
777 if (a != null && !String.IsNullOrEmpty (a.ValueName))
778 return new QName (a.ValueName, ContractNamespace);
779 return kvpair_value_qname;
782 internal virtual string CurrentNamespace {
784 string ns = item_qname.Namespace;
785 if (ns == KnownTypeCollection.MSSimpleNamespace)
786 ns = KnownTypeCollection.MSArraysNamespace;
792 PropertyInfo pair_key_property, pair_value_property;
794 public override void SerializeNonReference (object graph,
795 XmlFormatterSerializer serializer)
797 if (add_method != null) { // generic
798 if (pair_type == null) {
799 pair_type = typeof (KeyValuePair<,>).MakeGenericType (add_method.DeclaringType.GetGenericArguments ());
800 pair_key_property = pair_type.GetProperty ("Key");
801 pair_value_property = pair_type.GetProperty ("Value");
803 foreach (object p in (IEnumerable) graph) {
804 serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
805 serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
806 serializer.Serialize (pair_key_property.PropertyType, pair_key_property.GetValue (p, null));
807 serializer.WriteEndElement ();
808 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
809 serializer.Serialize (pair_value_property.PropertyType, pair_value_property.GetValue (p, null));
810 serializer.WriteEndElement ();
811 serializer.WriteEndElement ();
813 } else { // non-generic
814 foreach (DictionaryEntry p in (IEnumerable) graph) {
815 serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
816 serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
817 serializer.Serialize (key_type, p.Key);
818 serializer.WriteEndElement ();
819 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
820 serializer.Serialize (value_type, p.Value);
821 serializer.WriteEndElement ();
822 serializer.WriteEndElement ();
827 public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
829 #if NET_2_1 // FIXME: is it fine?
830 object instance = Activator.CreateInstance (RuntimeType);
832 object instance = Activator.CreateInstance (RuntimeType, true);
834 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
835 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
836 if (reader.IsEmptyElement)
837 throw new XmlException (String.Format ("Unexpected empty element for dictionary entry: name {0}", reader.Name));
838 // FIXME: sloppy parsing
839 reader.ReadStartElement ();// item_qname.Name, item_qname.Namespace);
840 reader.MoveToContent ();
841 object key = deserializer.Deserialize (key_type, reader);
842 reader.MoveToContent ();
843 object val = deserializer.Deserialize (value_type, reader);
844 reader.ReadEndElement (); // of pair
846 if (instance is IDictionary)
847 ((IDictionary)instance).Add (key, val);
848 else if (add_method != null)
849 add_method.Invoke (instance, new object [] {key, val});
851 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
856 public override List<DataMemberInfo> GetMembers ()
858 //Shouldn't come here at all!
859 throw new NotImplementedException ();
863 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
865 throw new NotImplementedException ();
870 internal class SharedTypeMap : SerializationMap
872 public SharedTypeMap (
873 Type type, QName qname, KnownTypeCollection knownTypes)
874 : base (type, qname, knownTypes)
876 Members = GetMembers (type, XmlName, false);
879 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
881 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
883 BindingFlags flags = AllInstanceFlags;
885 flags |= BindingFlags.DeclaredOnly;
887 foreach (FieldInfo fi in type.GetFields (flags)) {
888 if (fi.GetCustomAttributes (
889 typeof (NonSerializedAttribute),
894 throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
895 DataMemberAttribute dma = new DataMemberAttribute ();
897 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
903 // Does this make sense? I doubt.
904 public override List<DataMemberInfo> GetMembers ()
906 return GetMembers (RuntimeType, XmlName, true);
910 internal class EnumMap : SerializationMap
912 List<EnumMemberInfo> enum_members;
915 Type type, QName qname, KnownTypeCollection knownTypes)
916 : base (type, qname, knownTypes)
919 object [] atts = RuntimeType.GetCustomAttributes (
920 typeof (DataContractAttribute), false);
921 if (atts.Length != 0)
924 enum_members = new List<EnumMemberInfo> ();
925 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
927 foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
928 string name = fi.Name;
930 EnumMemberAttribute ema =
931 GetEnumMemberAttribute (fi);
935 if (ema.Value != null)
939 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
943 private EnumMemberAttribute GetEnumMemberAttribute (
946 object [] atts = mi.GetCustomAttributes (
947 typeof (EnumMemberAttribute), false);
948 if (atts.Length == 0)
950 return (EnumMemberAttribute) atts [0];
954 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
956 if (generated_schema_types.ContainsKey (XmlName))
957 return generated_schema_types [XmlName];
959 XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType ();
960 simpleType.Name = XmlName.Name;
962 XmlSchemaSimpleTypeRestriction simpleRestriction = new XmlSchemaSimpleTypeRestriction ();
963 simpleType.Content = simpleRestriction;
964 simpleRestriction.BaseTypeName = new XmlQualifiedName ("string", XmlSchema.Namespace);
966 foreach (EnumMemberInfo emi in enum_members) {
967 XmlSchemaEnumerationFacet e = new XmlSchemaEnumerationFacet ();
968 e.Value = emi.XmlName;
969 simpleRestriction.Facets.Add (e);
972 generated_schema_types [XmlName] = simpleType;
974 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
975 XmlSchemaElement element = GetSchemaElement (XmlName, simpleType);
976 element.IsNillable = true;
978 schema.Items.Add (simpleType);
979 schema.Items.Add (element);
985 public override void Serialize (object graph,
986 XmlFormatterSerializer serializer)
988 foreach (EnumMemberInfo emi in enum_members) {
989 if (Enum.Equals (emi.Value, graph)) {
990 serializer.Writer.WriteString (emi.XmlName);
995 throw new SerializationException (String.Format (
996 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
999 public override object DeserializeContent (XmlReader reader,
1000 XmlFormatterDeserializer deserializer)
1002 string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
1004 if (value != String.Empty) {
1005 foreach (EnumMemberInfo emi in enum_members)
1006 if (emi.XmlName == value)
1010 throw new SerializationException (String.Format (
1011 "Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
1015 internal struct EnumMemberInfo
1017 public readonly string XmlName;
1018 public readonly object Value;
1020 public EnumMemberInfo (string name, object value)
1027 internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
1029 public readonly int Order;
1030 public readonly bool IsRequired;
1031 public readonly string XmlName;
1032 public readonly MemberInfo Member;
1033 public readonly string XmlNamespace;
1034 public readonly string XmlRootNamespace;
1035 public readonly Type MemberType;
1037 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
1040 throw new ArgumentNullException ("dma");
1043 IsRequired = dma.IsRequired;
1044 XmlName = dma.Name != null ? dma.Name : member.Name;
1046 XmlRootNamespace = rootNamespce;
1047 if (Member is FieldInfo)
1048 MemberType = ((FieldInfo) Member).FieldType;
1050 MemberType = ((PropertyInfo) Member).PropertyType;
1053 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
1054 , IComparer // see bug #76361
1056 public static readonly DataMemberInfoComparer Instance
1057 = new DataMemberInfoComparer ();
1059 private DataMemberInfoComparer () {}
1061 public int Compare (object o1, object o2)
1063 return Compare ((DataMemberInfo) o1,
1064 (DataMemberInfo) o2);
1067 public int Compare (DataMemberInfo d1, DataMemberInfo d2)
1069 if (d1.Order == -1 || d2.Order == -1)
1070 return String.CompareOrdinal (d1.XmlName, d2.XmlName);
1072 return d1.Order - d2.Order;