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
> ();
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 public virtual object DeserializeObject (XmlReader reader
, XmlFormatterDeserializer deserializer
)
342 bool isEmpty
= reader
.IsEmptyElement
;
343 reader
.ReadStartElement ();
344 reader
.MoveToContent ();
349 res
= DeserializeEmptyContent (reader
, deserializer
);
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}",
362 RuntimeType
.FullName
,
363 li
!= null && li
.HasLineInfo () ? String
.Format (" {0}({1},{2})", reader
.BaseURI
, li
.LineNumber
, li
.LinePosition
) : String
.Empty
));
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;
392 for (; i
< Members
.Count
; i
++) { // unordered
393 if (Members
[i
].Order
>= 0)
395 if (reader
.LocalName
== Members
[i
].XmlName
&&
396 reader
.NamespaceURI
== Members
[i
].XmlRootNamespace
) {
402 for (i
= Math
.Max (i
, ordered
); i
< Members
.Count
; i
++) { // ordered
405 if (reader
.LocalName
== Members
[i
].XmlName
&&
406 reader
.NamespaceURI
== Members
[i
].XmlRootNamespace
) {
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
);
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
),
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)
443 if (dmi
.Member
is PropertyInfo
)
444 ((PropertyInfo
) dmi
.Member
).SetValue (obj
, value, null);
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);
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
;
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
);
489 // FIXME: verify return value sanity.
490 public override XmlSchemaType
GetSchemaType (XmlSchemaSet schemas
, Dictionary
<QName
, XmlSchemaType
> generated_schema_types
)
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
);
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
;
530 flags
|= BindingFlags
.DeclaredOnly
;
532 foreach (PropertyInfo pi
in type
.GetProperties (flags
)) {
533 DataMemberAttribute dma
=
534 GetDataMemberAttribute (pi
);
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
);
550 data_members
.Add (CreateDataMemberInfo (dma
, fi
, fi
.FieldType
));
556 public override List
<DataMemberInfo
> GetMembers ()
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 ()) {
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
;
582 if (mi
.GetCustomAttributes (typeof (IgnoreDataMemberAttribute
), false).Length
!= 0)
584 l
.Add (new DataMemberInfo (mi
, new DataMemberAttribute (), null, null));
586 l
.Sort (DataMemberInfo
.DataMemberInfoComparer
.Instance
);
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
)
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
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
);
630 if (RuntimeType
.IsInterface
) {
631 add_method
= RuntimeType
.GetMethod ("Add", icoll
.GetGenericArguments ());
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
];
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
<>))
654 public override bool OutputXsiType
{
655 get { return false; }
658 internal virtual string CurrentNamespace
{
660 string ns
= element_qname
.Namespace
;
661 if (ns
== KnownTypeCollection
.MSSimpleNamespace
)
662 ns
= KnownTypeCollection
.MSArraysNamespace
;
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
);
685 return Activator
.CreateInstance (typeof (List
<>).MakeGenericType (RuntimeType
.GetGenericArguments () [0])); // List<T>
687 return new ArrayList ();
689 #if NET_2_1 // FIXME: is it fine?
690 return Activator
.CreateInstance (RuntimeType
);
692 return Activator
.CreateInstance (RuntimeType
, true);
696 public override object DeserializeEmptyContent (XmlReader reader
, XmlFormatterDeserializer deserializer
)
698 var instance
= CreateInstance ();
699 if (RuntimeType
.IsArray
)
700 return ((ArrayList
)instance
).ToArray (element_type
);
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}
);
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
);
724 public override List
<DataMemberInfo
> GetMembers ()
726 //Shouldn't come here at all!
727 throw new NotImplementedException ();
731 public override XmlSchemaType
GetSchemaType (XmlSchemaSet schemas
, Dictionary
<QName
, XmlSchemaType
> generated_schema_types
)
733 if (generated_schema_types
.ContainsKey (XmlName
))
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
;
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
)
788 key_type
= typeof (object);
789 value_type
= typeof (object);
791 var idic
= GetGenericDictionaryInterface (RuntimeType
);
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
];
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
<,>))
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
{
861 string ns
= item_qname
.Namespace
;
862 if (ns
== KnownTypeCollection
.MSSimpleNamespace
)
863 ns
= KnownTypeCollection
.MSArraysNamespace
;
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>
912 return new Hashtable ();
914 #if NET_2_1 // FIXME: is it fine?
915 return Activator
.CreateInstance (RuntimeType
);
917 return Activator
.CreateInstance (RuntimeType
, true);
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}
);
946 throw new NotImplementedException (String
.Format ("Type {0} is not supported", RuntimeType
));
951 public override List
<DataMemberInfo
> GetMembers ()
953 //Shouldn't come here at all!
954 throw new NotImplementedException ();
958 public override XmlSchemaType
GetSchemaType (XmlSchemaSet schemas
, Dictionary
<QName
, XmlSchemaType
> generated_schema_types
)
960 throw new NotImplementedException ();
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
;
983 flags
|= BindingFlags
.DeclaredOnly
;
985 foreach (FieldInfo fi
in type
.GetFields (flags
)) {
986 if (fi
.GetCustomAttributes (
987 typeof (NonSerializedAttribute
),
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.
1002 // Does this make sense? I doubt.
1003 public override List
<DataMemberInfo
> GetMembers ()
1006 //return GetMembers (RuntimeType, XmlName, true);
1010 internal partial class EnumMap
: SerializationMap
1012 List
<EnumMemberInfo
> enum_members
;
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)
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
;
1032 EnumMemberAttribute ema
=
1033 GetEnumMemberAttribute (fi
);
1037 if (ema
.Value
!= null)
1041 enum_members
.Add (new EnumMemberInfo (name
, fi
.GetValue (null)));
1045 private EnumMemberAttribute
GetEnumMemberAttribute (
1048 object [] atts
= mi
.GetCustomAttributes (
1049 typeof (EnumMemberAttribute
), false);
1050 if (atts
.Length
== 0)
1052 return (EnumMemberAttribute
) atts
[0];
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
);
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
);
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
)
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)
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)
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
)
1151 throw new ArgumentNullException ("dma");
1154 IsRequired
= dma
.IsRequired
;
1155 XmlName
= dma
.Name
!= null ? dma
.Name
: member
.Name
;
1157 XmlRootNamespace
= rootNamespce
;
1158 if (Member
is FieldInfo
)
1159 MemberType
= ((FieldInfo
) Member
).FieldType
;
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
;