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 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;
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 == null)
104 qname = new QName (qname.Name,
105 KnownTypeCollection.DefaultClrNamespaceBase + type.Namespace);
108 Members = new List<DataMemberInfo> ();
110 foreach (var mi in type.GetMethods (AllInstanceFlags)) {
111 if (mi.GetCustomAttributes (typeof (OnDeserializingAttribute), false).Length > 0)
112 OnDeserializing = mi;
113 else if (mi.GetCustomAttributes (typeof (OnDeserializedAttribute), false).Length > 0)
118 public MethodInfo OnDeserializing { get; set; }
119 public MethodInfo OnDeserialized { get; set; }
121 public virtual bool OutputXsiType {
125 public QName XmlName { get; set; }
127 public abstract bool IsContractAllowedType { get; }
129 protected void HandleId (XmlReader reader, XmlFormatterDeserializer deserializer, object instance)
131 HandleId (reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace), deserializer, instance);
134 protected void HandleId (string id, XmlFormatterDeserializer deserializer, object instance)
137 deserializer.References.Add (id, instance);
140 public CollectionDataContractAttribute GetCollectionDataContractAttribute (Type type)
142 object [] atts = type.GetCustomAttributes (
143 typeof (CollectionDataContractAttribute), false);
144 return atts.Length == 0 ? null : (CollectionDataContractAttribute) atts [0];
147 public DataMemberAttribute GetDataMemberAttribute (
150 object [] atts = mi.GetCustomAttributes (
151 typeof (DataMemberAttribute), false);
152 if (atts.Length == 0)
154 return (DataMemberAttribute) atts [0];
157 bool IsPrimitive (Type type)
159 return (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object));
162 //Returns list of data members for this type ONLY
163 public virtual List<DataMemberInfo> GetMembers ()
165 throw new NotImplementedException (String.Format ("Implement me for {0}", this));
169 protected XmlSchemaElement GetSchemaElement (QName qname, XmlSchemaType schemaType)
171 XmlSchemaElement schemaElement = new XmlSchemaElement ();
172 schemaElement.Name = qname.Name;
173 schemaElement.SchemaTypeName = qname;
175 if (schemaType is XmlSchemaComplexType)
176 schemaElement.IsNillable = true;
178 return schemaElement;
181 protected XmlSchema GetSchema (XmlSchemaSet schemas, string ns)
183 ICollection colln = schemas.Schemas (ns);
184 if (colln.Count > 0) {
186 throw new Exception (String.Format (
187 "More than 1 schema for namespace '{0}' found.", ns));
188 foreach (object o in colln)
190 return (o as XmlSchema);
193 XmlSchema schema = new XmlSchema ();
194 schema.TargetNamespace = ns;
195 schema.ElementFormDefault = XmlSchemaForm.Qualified;
196 schemas.Add (schema);
201 protected XmlQualifiedName GetQualifiedName (Type type)
203 if (qname_table.ContainsKey (type))
204 return qname_table [type];
206 QName qname = KnownTypes.GetQName (type);
207 if (qname.Namespace == KnownTypeCollection.MSSimpleNamespace)
208 qname = new QName (qname.Name, XmlSchema.Namespace);
210 qname_table [type] = qname;
215 public virtual void Serialize (object graph,
216 XmlFormatterSerializer serializer)
219 if (serializer.TrySerializeAsReference (IsReference, graph, out label))
221 else if (serializer.SerializingObjects.Contains (graph))
222 throw new SerializationException (String.Format ("Circular reference of an object in the object graph was found: '{0}' of type {1}", graph, graph.GetType ()));
223 serializer.SerializingObjects.Add (graph);
226 serializer.Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
228 SerializeNonReference (graph, serializer);
230 serializer.SerializingObjects.Remove (graph);
233 public virtual void SerializeNonReference (object graph,
234 XmlFormatterSerializer serializer)
236 foreach (DataMemberInfo dmi in Members) {
237 FieldInfo fi = dmi.Member as FieldInfo;
238 PropertyInfo pi = fi == null ?
239 (PropertyInfo) dmi.Member : null;
240 Type type = fi != null ?
241 fi.FieldType : pi.PropertyType;
242 object value = fi != null ?
243 fi.GetValue (graph) :
244 pi.GetValue (graph, null);
246 serializer.WriteStartElement (dmi.XmlName, dmi.XmlRootNamespace, dmi.XmlNamespace);
247 serializer.Serialize (type, value);
248 serializer.WriteEndElement ();
252 public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
254 bool isEmpty = reader.IsEmptyElement;
255 string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
256 reader.ReadStartElement ();
257 reader.MoveToContent ();
262 res = DeserializeEmptyContent (reader, deserializer, id);
264 res = DeserializeContent (reader, deserializer, id);
266 reader.MoveToContent ();
267 if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
268 reader.ReadEndElement ();
269 else if (!isEmpty && !reader.EOF && reader.NodeType != XmlNodeType.EndElement) {
270 var li = reader as IXmlLineInfo;
271 throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.{4}",
275 RuntimeType.FullName,
276 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : String.Empty));
281 // This is sort of hack. The argument reader already moved ahead of
282 // the actual empty element.It's just for historical consistency.
283 public virtual object DeserializeEmptyContent (XmlReader reader,
284 XmlFormatterDeserializer deserializer, string id)
286 return DeserializeContent (reader, deserializer, id, true);
289 public virtual object DeserializeContent (XmlReader reader,
290 XmlFormatterDeserializer deserializer, string id)
292 return DeserializeContent (reader, deserializer, id, false);
295 object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id, bool empty)
297 object instance = FormatterServices.GetUninitializedObject (RuntimeType);
298 HandleId (id, deserializer, instance);
300 if (OnDeserializing != null)
301 OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
303 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
304 bool [] filled = new bool [Members.Count];
305 bool [] nsmatched = new bool [Members.Count];
306 int memberInd = -1, ordered = -1;
307 while (!empty && reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
308 DataMemberInfo dmi = null;
310 bool nsmatchedOne = false;
311 for (; i < Members.Count; i++) { // unordered
312 if (Members [i].Order >= 0)
314 if (reader.LocalName == Members [i].XmlName) {
317 nsmatchedOne = (dmi.XmlRootNamespace == null || reader.NamespaceURI == dmi.XmlRootNamespace);
322 for (i = Math.Max (i, ordered); i < Members.Count; i++) { // ordered
325 if (reader.LocalName == Members [i].XmlName) {
329 nsmatchedOne = (dmi.XmlRootNamespace == null || reader.NamespaceURI == dmi.XmlRootNamespace);
337 reader.MoveToContent ();
340 if (filled [memberInd] && nsmatched [memberInd]) {
341 // The strictly-corresponding member (i.e. that matches namespace URI too, not only local name) already exists, so skip this element.
343 reader.MoveToContent ();
346 nsmatched [memberInd] = nsmatchedOne;
347 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
348 filled [memberInd] = true;
349 reader.MoveToContent ();
351 for (int i = 0; i < Members.Count; i++)
352 if (!filled [i] && Members [i].IsRequired)
353 throw MissingRequiredMember (Members [i], reader);
355 if (OnDeserialized != null)
356 OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
361 // For now it could be private.
362 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
364 var li = reader as IXmlLineInfo;
365 return new ArgumentException (String.Format ("Data contract member {0} for the type {1} is required, but missing in the input XML.{2}",
366 new QName (dmi.XmlName, dmi.XmlNamespace),
368 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : null));
371 // For now it could be private.
372 protected void SetValue (DataMemberInfo dmi, object obj, object value)
375 if (dmi.Member is PropertyInfo)
376 ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
378 ((FieldInfo) dmi.Member).SetValue (obj, value);
379 } catch (Exception ex) {
380 throw new InvalidOperationException (String.Format ("Failed to set value of type {0} for property {1}", value != null ? value.GetType () : null, dmi.Member), ex);
384 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type memberType, string ownerNamespace)
386 KnownTypes.Add (memberType);
387 QName qname = KnownTypes.GetQName (memberType);
389 if (KnownTypeCollection.GetPrimitiveTypeFromName (qname) != null)
390 return new DataMemberInfo (mi, dma, ownerNamespace, null);
392 return new DataMemberInfo (mi, dma, ownerNamespace, qname.Namespace);
396 internal partial class XmlSerializableMap : SerializationMap
398 public override bool IsContractAllowedType { get { return true; } }
400 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
401 : base (type, qname, knownTypes)
405 public override void Serialize (object graph, XmlFormatterSerializer serializer)
407 IXmlSerializable ixs = graph as IXmlSerializable;
409 //FIXME: Throw what exception here?
410 throw new SerializationException ();
412 ixs.WriteXml (serializer.Writer);
415 public override object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
418 IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType);
420 IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType, true);
423 HandleId (reader, deserializer, ixs);
425 ixs.ReadXml (reader);
430 internal partial class SharedContractMap : SerializationMap
432 public SharedContractMap (
433 Type type, QName qname, KnownTypeCollection knownTypes)
434 : base (type, qname, knownTypes)
438 public override bool IsContractAllowedType { get { return true; } }
440 internal void Initialize ()
442 Type type = RuntimeType;
443 List <DataMemberInfo> members;
444 object [] atts = type.GetCustomAttributes (
445 typeof (DataContractAttribute), false);
446 IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
448 while (type != null) {
449 members = GetMembers (type);
450 members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
451 Members.InsertRange (0, members);
454 type = type.BaseType;
458 List<DataMemberInfo> GetMembers (Type type)
460 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
461 BindingFlags flags = AllInstanceFlags | BindingFlags.DeclaredOnly;
463 foreach (PropertyInfo pi in type.GetProperties (flags)) {
464 DataMemberAttribute dma =
465 GetDataMemberAttribute (pi);
468 KnownTypes.Add (pi.PropertyType);
469 var map = KnownTypes.FindUserMap (pi.PropertyType);
470 if (!pi.CanRead || (!pi.CanWrite && !(map is ICollectionTypeMap)))
471 throw new InvalidDataContractException (String.Format (
472 "DataMember property '{0}' on type '{1}' must have both getter and setter.", pi, pi.DeclaringType));
473 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType, KnownTypeCollection.GetStaticQName (pi.DeclaringType).Namespace));
476 foreach (FieldInfo fi in type.GetFields (flags)) {
477 DataMemberAttribute dma =
478 GetDataMemberAttribute (fi);
481 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType, KnownTypeCollection.GetStaticQName (fi.DeclaringType).Namespace));
487 public override List<DataMemberInfo> GetMembers ()
493 internal partial class DefaultTypeMap : SerializationMap
495 public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
496 : base (type, KnownTypeCollection.GetStaticQName (type), knownTypes)
500 public override bool IsContractAllowedType { get { return false; } }
502 internal void Initialize ()
504 Members.AddRange (GetDefaultMembers ());
507 List<DataMemberInfo> GetDefaultMembers ()
509 var l = new List<DataMemberInfo> ();
510 foreach (var mi in RuntimeType.GetMembers ()) {
512 FieldInfo fi = mi as FieldInfo;
513 mt = fi == null ? null : fi.FieldType;
514 PropertyInfo pi = mi as PropertyInfo;
515 if (pi != null && pi.CanRead && pi.CanWrite && pi.GetIndexParameters ().Length == 0)
516 mt = pi.PropertyType;
519 if (mi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length != 0)
521 string ns = KnownTypeCollection.GetStaticQName (mi.DeclaringType).Namespace;
522 l.Add (CreateDataMemberInfo (new DataMemberAttribute (), mi, mt, ns));
524 l.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
529 // FIXME: it still needs to consider KeyName/ValueName
530 // (especially Dictionary collection is not likely considered yet.)
531 internal partial class CollectionContractTypeMap : CollectionTypeMap
533 CollectionDataContractAttribute a;
535 public CollectionContractTypeMap (
536 Type type, CollectionDataContractAttribute a, Type elementType,
537 QName qname, KnownTypeCollection knownTypes)
538 : base (type, elementType, qname, knownTypes)
541 IsReference = a.IsReference;
542 if (!String.IsNullOrEmpty (a.ItemName))
543 element_qname = new XmlQualifiedName (a.ItemName, a.Namespace ?? CurrentNamespace);
546 internal override string CurrentNamespace {
547 get { return XmlName.Namespace; }
550 public override bool IsContractAllowedType { get { return true; } }
553 internal interface ICollectionTypeMap
557 internal partial class CollectionTypeMap : SerializationMap, ICollectionTypeMap
560 internal QName element_qname;
561 MethodInfo add_method;
563 public CollectionTypeMap (
564 Type type, Type elementType,
565 QName qname, KnownTypeCollection knownTypes)
566 : base (type, qname, knownTypes)
568 element_type = elementType;
569 element_qname = KnownTypes.GetQName (element_type);
570 var icoll = GetGenericCollectionInterface (RuntimeType);
572 if (RuntimeType.IsInterface) {
573 add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
575 var imap = RuntimeType.GetInterfaceMap (icoll);
576 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
577 if (imap.InterfaceMethods [i].Name == "Add") {
578 add_method = imap.TargetMethods [i];
581 if (add_method == null)
582 add_method = type.GetMethod ("Add", icoll.GetGenericArguments ());
587 static Type GetGenericCollectionInterface (Type type)
589 foreach (var iface in type.GetInterfacesOrSelfInterface ())
590 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IEnumerable<>))
596 public override bool IsContractAllowedType { get { return false; } }
598 public override bool OutputXsiType {
599 get { return false; }
602 internal virtual string CurrentNamespace {
604 string ns = element_qname.Namespace;
605 if (ns == KnownTypeCollection.MSSimpleNamespace)
606 ns = KnownTypeCollection.MSArraysNamespace;
611 public override void SerializeNonReference (object graph,
612 XmlFormatterSerializer serializer)
614 // output item xmlns in advance so that it does not result in excessive xmlns overwrites.
615 if (XmlName.Namespace != element_qname.Namespace && element_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
616 serializer.Writer.WriteXmlnsAttribute (null, element_qname.Namespace);
618 foreach (object o in (IEnumerable) graph) {
619 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, CurrentNamespace);
620 serializer.Serialize (element_type, o);
621 serializer.WriteEndElement ();
625 object CreateInstance ()
627 if (RuntimeType.IsArray)
628 return new ArrayList ();
629 if (RuntimeType.IsInterface) {
630 var icoll = GetGenericCollectionInterface (RuntimeType);
632 return Activator.CreateInstance (typeof (List<>).MakeGenericType (RuntimeType.GetGenericArguments () [0])); // List<T>
634 return new ArrayList ();
636 #if NET_2_1 // FIXME: is it fine?
637 return Activator.CreateInstance (RuntimeType);
639 return Activator.CreateInstance (RuntimeType, true);
643 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
645 var instance = CreateInstance ();
646 HandleId (id, deserializer, instance);
647 if (OnDeserializing != null)
648 OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
650 if (RuntimeType.IsArray)
651 return ((ArrayList)instance).ToArray (element_type);
655 if (OnDeserialized != null)
656 OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
660 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
662 object instance = CreateInstance ();
663 HandleId (id, deserializer, instance);
664 if (OnDeserializing != null)
665 OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
666 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
667 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
668 object elem = deserializer.Deserialize (element_type, reader);
669 if (instance is IList)
670 ((IList)instance).Add (elem);
671 else if (add_method != null)
672 add_method.Invoke (instance, new object [] {elem});
674 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
675 reader.MoveToContent ();
678 if (RuntimeType.IsArray)
679 return ((ArrayList)instance).ToArray (element_type);
682 if (OnDeserialized != null)
683 OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
687 public override List<DataMemberInfo> GetMembers ()
689 //Shouldn't come here at all!
690 throw new NotImplementedException ();
694 internal partial class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
696 Type key_type, value_type;
697 QName item_qname, key_qname, value_qname;
698 MethodInfo add_method;
699 CollectionDataContractAttribute a;
701 public DictionaryTypeMap (
702 Type type, CollectionDataContractAttribute a, KnownTypeCollection knownTypes)
703 : base (type, QName.Empty, knownTypes)
707 key_type = typeof (object);
708 value_type = typeof (object);
710 var idic = GetGenericDictionaryInterface (RuntimeType);
712 var imap = RuntimeType.GetInterfaceMap (idic);
713 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
714 if (imap.InterfaceMethods [i].Name == "Add") {
715 add_method = imap.TargetMethods [i];
718 var argtypes = idic.GetGenericArguments();
719 key_type = argtypes [0];
720 value_type = argtypes [1];
721 if (add_method == null)
722 add_method = type.GetMethod ("Add", argtypes);
725 XmlName = GetDictionaryQName ();
726 item_qname = GetItemQName ();
727 key_qname = GetKeyQName ();
728 value_qname = GetValueQName ();
731 static Type GetGenericDictionaryInterface (Type type)
733 foreach (var iface in type.GetInterfacesOrSelfInterface ())
734 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>))
740 string ContractNamespace {
741 get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
744 public override bool IsContractAllowedType { get { return a != null; } }
746 public Type KeyType { get { return key_type; } }
747 public Type ValueType { get { return value_type; } }
749 internal virtual QName GetDictionaryQName ()
751 string name = a != null ? a.Name : null;
752 string ns = a != null ? a.Namespace : null;
753 if (RuntimeType.IsGenericType && RuntimeType.GetGenericTypeDefinition () != typeof (Dictionary<,>))
754 name = name ?? KnownTypeCollection.GetDefaultName (RuntimeType);
756 name = "ArrayOf" + GetItemQName ().Name;
757 ns = ns ?? KnownTypeCollection.MSArraysNamespace;
759 return new QName (name, ns);
762 internal virtual QName GetItemQName ()
764 string name = a != null ? a.ItemName : null;
765 string ns = a != null ? a.Namespace : null;
767 name = name ?? "KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name;
768 ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
770 return new QName (name, ns);
773 internal virtual QName GetKeyQName ()
775 string name = a != null ? a.KeyName : null;
776 string ns = a != null ? a.Namespace : null;
778 name = name ?? "Key";
779 ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
780 return new QName (name, ns);
783 internal virtual QName GetValueQName ()
785 string name = a != null ? a.ValueName : null;
786 string ns = a != null ? a.Namespace : null;
788 name = name ?? "Value";
789 ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
790 return new QName (name, ns);
793 internal virtual string CurrentNamespace {
795 string ns = item_qname.Namespace;
796 if (ns == KnownTypeCollection.MSSimpleNamespace)
797 ns = KnownTypeCollection.MSArraysNamespace;
803 PropertyInfo pair_key_property, pair_value_property;
805 public override void SerializeNonReference (object graph,
806 XmlFormatterSerializer serializer)
808 if (add_method != null) { // generic
809 if (pair_type == null) {
810 pair_type = typeof (KeyValuePair<,>).MakeGenericType (add_method.DeclaringType.GetGenericArguments ());
811 pair_key_property = pair_type.GetProperty ("Key");
812 pair_value_property = pair_type.GetProperty ("Value");
814 foreach (object 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 (pair_key_property.PropertyType, pair_key_property.GetValue (p, null));
818 serializer.WriteEndElement ();
819 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
820 serializer.Serialize (pair_value_property.PropertyType, pair_value_property.GetValue (p, null));
821 serializer.WriteEndElement ();
822 serializer.WriteEndElement ();
824 } else { // non-generic
825 foreach (DictionaryEntry p in (IEnumerable) graph) {
826 serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
827 serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
828 serializer.Serialize (key_type, p.Key);
829 serializer.WriteEndElement ();
830 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
831 serializer.Serialize (value_type, p.Value);
832 serializer.WriteEndElement ();
833 serializer.WriteEndElement ();
838 object CreateInstance ()
840 if (RuntimeType.IsInterface) {
841 if (RuntimeType.IsGenericType && Array.IndexOf (RuntimeType.GetGenericTypeDefinition ().GetInterfaces (), typeof (IDictionary<,>)) >= 0) {
842 var gargs = RuntimeType.GetGenericArguments ();
843 return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (gargs [0], gargs [1])); // Dictionary<T>
846 return new Hashtable ();
848 #if NET_2_1 // FIXME: is it fine?
849 return Activator.CreateInstance (RuntimeType);
851 return Activator.CreateInstance (RuntimeType, true);
855 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
857 return DeserializeContent (reader, deserializer, id);
860 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
862 object instance = CreateInstance ();
863 HandleId (id, deserializer, instance);
864 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
865 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
866 if (reader.IsEmptyElement)
867 throw new XmlException (String.Format ("Unexpected empty element for dictionary entry: name {0}", reader.Name));
868 // FIXME: sloppy parsing
869 reader.ReadStartElement ();// item_qname.Name, item_qname.Namespace);
870 reader.MoveToContent ();
871 object key = deserializer.Deserialize (key_type, reader);
872 reader.MoveToContent ();
873 object val = deserializer.Deserialize (value_type, reader);
874 reader.MoveToContent ();
875 reader.ReadEndElement (); // of pair
876 reader.MoveToContent ();
878 if (instance is IDictionary)
879 ((IDictionary)instance).Add (key, val);
880 else if (add_method != null)
881 add_method.Invoke (instance, new object [] {key, val});
883 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
888 public override List<DataMemberInfo> GetMembers ()
890 //Shouldn't come here at all!
891 throw new NotImplementedException ();
895 internal partial class SharedTypeMap : SerializationMap
897 public SharedTypeMap (
898 Type type, QName qname, KnownTypeCollection knownTypes)
899 : base (type, qname, knownTypes)
903 public override bool IsContractAllowedType { get { return true; } }
905 public void Initialize ()
907 Members = GetMembers (RuntimeType, XmlName, false);
910 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
912 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
913 BindingFlags flags = AllInstanceFlags | BindingFlags.DeclaredOnly;
915 for (Type t = type; t != null; t = t.BaseType) {
916 foreach (FieldInfo fi in t.GetFields (flags)) {
917 if (fi.GetCustomAttributes (
918 typeof (NonSerializedAttribute),
923 throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
924 DataMemberAttribute dma = new DataMemberAttribute ();
925 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType, KnownTypeCollection.GetStaticQName (fi.DeclaringType).Namespace));
929 data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance); // alphabetic order.
934 // Does this make sense? I doubt.
935 public override List<DataMemberInfo> GetMembers ()
938 //return GetMembers (RuntimeType, XmlName, true);
942 internal partial class EnumMap : SerializationMap
944 List<EnumMemberInfo> enum_members;
948 Type type, QName qname, KnownTypeCollection knownTypes)
949 : base (type, qname, knownTypes)
952 object [] atts = RuntimeType.GetCustomAttributes (
953 typeof (DataContractAttribute), false);
954 if (atts.Length != 0)
956 flag_attr = type.GetCustomAttributes (typeof (FlagsAttribute), false).Length > 0;
958 enum_members = new List<EnumMemberInfo> ();
959 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
961 foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
962 string name = fi.Name;
964 EnumMemberAttribute ema =
965 GetEnumMemberAttribute (fi);
969 if (ema.Value != null)
973 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
977 public override bool IsContractAllowedType { get { return false; } }
979 private EnumMemberAttribute GetEnumMemberAttribute (
982 object [] atts = mi.GetCustomAttributes (
983 typeof (EnumMemberAttribute), false);
984 if (atts.Length == 0)
986 return (EnumMemberAttribute) atts [0];
989 public override void Serialize (object graph,
990 XmlFormatterSerializer serializer)
993 long val = Convert.ToInt64 (graph);
995 foreach (EnumMemberInfo emi in enum_members) {
996 long f = Convert.ToInt64 (emi.Value);
998 s += (s != null ? " " : String.Empty) + emi.XmlName;
1001 serializer.Writer.WriteString (s);
1004 foreach (EnumMemberInfo emi in enum_members) {
1005 if (Enum.Equals (emi.Value, graph)) {
1006 serializer.Writer.WriteString (emi.XmlName);
1012 throw new SerializationException (String.Format (
1013 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
1016 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
1019 throw new SerializationException (String.Format ("Enum value '' is invalid for type '{0}' and cannot be deserialized.", RuntimeType));
1020 var instance = Enum.ToObject (RuntimeType, 0);
1021 HandleId (id, deserializer, instance);
1025 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
1027 string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
1028 HandleId (id, deserializer, value);
1030 if (value != String.Empty) {
1031 if (flag_attr && value.IndexOf (' ') != -1) {
1033 foreach (string flag in value.Split (' ')) {
1034 foreach (EnumMemberInfo emi in enum_members) {
1035 if (emi.XmlName == flag) {
1036 flags |= Convert.ToInt64 (emi.Value);
1041 return Enum.ToObject (RuntimeType, flags);
1044 foreach (EnumMemberInfo emi in enum_members)
1045 if (emi.XmlName == value)
1051 throw new SerializationException (String.Format ("Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
1052 return Enum.ToObject (RuntimeType, 0);
1056 internal struct EnumMemberInfo
1058 public readonly string XmlName;
1059 public readonly object Value;
1061 public EnumMemberInfo (string name, object value)
1068 internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
1070 public readonly int Order;
1071 public readonly bool IsRequired;
1072 public readonly string XmlName;
1073 public readonly MemberInfo Member;
1074 public readonly string XmlNamespace;
1075 public readonly string XmlRootNamespace;
1076 public readonly Type MemberType;
1078 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
1081 throw new ArgumentNullException ("dma");
1084 IsRequired = dma.IsRequired;
1085 XmlName = XmlConvert.EncodeLocalName (dma.Name != null ? dma.Name : member.Name);
1087 XmlRootNamespace = rootNamespce;
1088 if (Member is FieldInfo)
1089 MemberType = ((FieldInfo) Member).FieldType;
1091 MemberType = ((PropertyInfo) Member).PropertyType;
1094 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
1095 , IComparer // see bug #76361
1097 public static readonly DataMemberInfoComparer Instance
1098 = new DataMemberInfoComparer ();
1100 private DataMemberInfoComparer () {}
1102 public int Compare (object o1, object o2)
1104 return Compare ((DataMemberInfo) o1,
1105 (DataMemberInfo) o2);
1108 public int Compare (DataMemberInfo d1, DataMemberInfo d2)
1110 if (d1.Order == d2.Order)
1111 return String.CompareOrdinal (d1.XmlName, d2.XmlName);
1113 return d1.Order - d2.Order;