2 // XmlFormatterDeserializer.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections.Generic;
33 using System.Reflection;
34 using System.Runtime.Serialization.Formatters.Binary;
36 using System.Xml.Schema;
38 using QName = System.Xml.XmlQualifiedName;
40 namespace System.Runtime.Serialization
42 internal class XmlFormatterDeserializer
44 KnownTypeCollection types;
45 IDataContractSurrogate surrogate;
46 DataContractResolver resolver, default_resolver; // new in 4.0.
47 // 3.5 SP1 supports deserialization by reference (id->obj).
48 // Though unlike XmlSerializer, it does not support forward-
49 // reference resolution i.e. a referenced object must appear
50 // before any references to it.
51 Dictionary<string,object> references = new Dictionary<string,object> ();
52 Dictionary<QName,Type> resolved_qnames = new Dictionary<QName,Type> ();
54 public static object Deserialize (XmlReader reader, Type declaredType,
55 KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, DataContractResolver resolver, DataContractResolver defaultResolver,
56 string name, string ns, bool verifyObjectName)
58 reader.MoveToContent ();
60 if (reader.NodeType != XmlNodeType.Element ||
61 reader.LocalName != name ||
62 reader.NamespaceURI != ns)
63 throw new SerializationException (String.Format ("Expected element '{0}' in namespace '{1}', but found {2} node '{3}' in namespace '{4}'", name, ns, reader.NodeType, reader.LocalName, reader.NamespaceURI));
64 // Verify (knownTypes, declaredType, name, ns, reader);
65 return new XmlFormatterDeserializer (knownTypes, surrogate, resolver, defaultResolver).Deserialize (declaredType, reader);
68 // Verify the top element name and namespace.
69 private static void Verify (KnownTypeCollection knownTypes, Type type, string name, string Namespace, XmlReader reader)
71 QName graph_qname = new QName (reader.LocalName, reader.NamespaceURI);
72 if (graph_qname.Name == name && graph_qname.Namespace == Namespace)
75 // <BClass .. i:type="EClass" >..</BClass>
76 // Expecting type EClass : allowed
77 // See test Serialize1b, and Serialize1c (for
80 // Run through inheritance heirarchy ..
81 for (Type baseType = type; baseType != null; baseType = baseType.BaseType)
82 if (knownTypes.GetQName (baseType) == graph_qname)
85 QName typeQName = knownTypes.GetQName (type);
86 throw new SerializationException (String.Format (
87 "Expecting element '{0}' from namespace '{1}'. Encountered 'Element' with name '{2}', namespace '{3}'",
88 typeQName.Name, typeQName.Namespace, graph_qname.Name, graph_qname.Namespace));
91 private XmlFormatterDeserializer (
92 KnownTypeCollection knownTypes,
93 IDataContractSurrogate surrogate,
94 DataContractResolver resolver,
95 DataContractResolver defaultResolver)
97 this.types = knownTypes;
98 this.surrogate = surrogate;
99 this.resolver = resolver;
100 this.default_resolver = defaultResolver;
103 public Dictionary<string,object> References {
104 get { return references; }
108 XmlDocument document;
110 XmlDocument XmlDocument {
111 get { return (document = document ?? new XmlDocument ()); }
115 // This method handles z:Ref, xsi:nil and primitive types, and then delegates to DeserializeByMap() for anything else.
117 public object Deserialize (Type type, XmlReader reader)
120 if (type == typeof (XmlElement))
121 return XmlDocument.ReadNode (reader);
122 else if (type == typeof (XmlNode [])) {
123 reader.ReadStartElement ();
124 var l = new List<XmlNode> ();
125 for(; !reader.EOF && reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ())
126 l.Add (XmlDocument.ReadNode (reader));
127 reader.ReadEndElement ();
131 QName graph_qname = null;
133 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>)) {
134 Type internal_type = type.GetGenericArguments () [0];
136 if (types.FindUserMap(internal_type) != null) {
137 graph_qname = types.GetQName (internal_type);
141 if (graph_qname == null)
142 graph_qname = types.GetQName (type);
144 string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
146 string [] parts = itype.Split (':');
147 if (parts.Length > 1)
148 graph_qname = new QName (parts [1], reader.LookupNamespace (reader.NameTable.Get (parts [0])));
150 graph_qname = new QName (itype, reader.LookupNamespace (String.Empty));
153 string label = reader.GetAttribute ("Ref", KnownTypeCollection.MSSimpleNamespace);
156 if (!references.TryGetValue (label, out o))
157 throw new SerializationException (String.Format ("Deserialized object with reference Id '{0}' was not found", label));
162 bool isNil = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace) == "true";
166 if (!type.IsValueType || type == typeof (void))
168 else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
171 throw new SerializationException (String.Format ("Value type {0} cannot be null.", type));
174 if (resolver != null) {
176 if (resolved_qnames.TryGetValue (graph_qname, out t))
178 else { // i.e. resolve name only once.
179 type = resolver.ResolveName (graph_qname.Name, graph_qname.Namespace, type, default_resolver) ?? type;
180 resolved_qnames.Add (graph_qname, type);
185 if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname) != null) {
186 string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
188 object ret = DeserializePrimitive (type, reader, graph_qname);
191 if (references.ContainsKey (id))
192 throw new InvalidOperationException (String.Format ("Object with Id '{0}' already exists as '{1}'", id, references [id]));
193 references.Add (id, ret);
198 return DeserializeByMap (graph_qname, type, reader);
201 object DeserializePrimitive (Type type, XmlReader reader, QName qname)
203 bool isDateTimeOffset = false;
204 // Handle DateTimeOffset type and DateTimeOffset?.
205 if (type == typeof (DateTimeOffset))
206 isDateTimeOffset = true;
207 else if(type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
208 isDateTimeOffset = type.GetGenericArguments () [0] == typeof (DateTimeOffset);
209 // It is the only exceptional type that does not serialize to string but serializes into complex element.
210 if (isDateTimeOffset) {
211 if (reader.IsEmptyElement) {
213 return default (DateTimeOffset);
215 reader.ReadStartElement ();
216 reader.MoveToContent ();
217 var date = reader.ReadElementContentAsDateTime ("DateTime", KnownTypeCollection.DefaultClrNamespaceSystem);
218 var off = TimeSpan.FromMinutes (reader.ReadElementContentAsInt ("OffsetMinutes", KnownTypeCollection.DefaultClrNamespaceSystem));
219 reader.MoveToContent ();
220 reader.ReadEndElement ();
221 return new DateTimeOffset (DateTime.SpecifyKind (date.ToUniversalTime () + off, DateTimeKind.Unspecified), off);
225 if (reader.IsEmptyElement) {
226 reader.Read (); // advance
227 if (type.IsValueType)
228 return Activator.CreateInstance (type);
230 // FIXME: Workaround for creating empty objects of the correct type.
231 value = String.Empty;
234 value = reader.ReadElementContentAsString ();
235 return KnownTypeCollection.PredefinedTypeStringToObject (value, qname.Name, reader);
238 object DeserializeByMap (QName name, Type type, XmlReader reader)
240 SerializationMap map = null;
241 // List<T> and T[] have the same QName, use type to find map work better.
242 if(name.Name.StartsWith ("ArrayOf", StringComparison.Ordinal) || resolved_qnames.ContainsKey (name))
243 map = types.FindUserMap (type);
245 map = types.FindUserMap (name); // use type when the name is "resolved" one. Otherwise use name (there are cases that type cannot be resolved by type).
246 if (map == null && (name.Name.StartsWith ("ArrayOf", StringComparison.Ordinal) ||
247 name.Namespace == KnownTypeCollection.MSArraysNamespace ||
248 name.Namespace.StartsWith (KnownTypeCollection.DefaultClrNamespaceBase, StringComparison.Ordinal))) {
249 var it = GetTypeFromNamePair (name.Name, name.Namespace);
251 map = types.FindUserMap (name);
254 throw new SerializationException (String.Format ("Unknown type {0} is used for DataContract with reference of name {1}. Any derived types of a data contract or a data member should be added to KnownTypes.", type, name));
256 return map.DeserializeObject (reader, this);
259 Type GetTypeFromNamePair (string name, string ns)
261 Type p = KnownTypeCollection.GetPrimitiveTypeFromName (new QName (name, ns));
264 bool makeArray = false;
265 if (name.StartsWith ("ArrayOf", StringComparison.Ordinal)) {
266 name = name.Substring (7); // strip "ArrayOf"
267 if (ns == KnownTypeCollection.MSArraysNamespace)
268 return GetTypeFromNamePair (name, String.Empty).MakeArrayType ();
272 string dnsb = KnownTypeCollection.DefaultClrNamespaceBase;
273 string clrns = ns.StartsWith (dnsb, StringComparison.Ordinal) ? ns.Substring (dnsb.Length) : ns;
275 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
280 types = ass.GetTypes ();
281 } catch (System.Reflection.ReflectionTypeLoadException rtle) {
285 types = ass.GetTypes ();
290 foreach (var t in types) {
291 // there can be null entries or exception throw to access the attribute -
292 // at least when some referenced assemblies could not be loaded (affects moonlight)
297 var dca = t.GetCustomAttribute<DataContractAttribute> (true);
298 if (dca != null && dca.Name == name && dca.Namespace == ns)
299 return makeArray ? t.MakeArrayType () : t;
301 catch (TypeLoadException tle) {
302 Console.Error.WriteLine (tle);
305 catch (FileNotFoundException fnfe) {
306 Console.Error.WriteLine (fnfe);
310 if (clrns != null && t.Name == name && t.Namespace == clrns)
311 return makeArray ? t.MakeArrayType () : t;
314 throw new XmlException (String.Format ("Type not found; name: {0}, namespace: {1}", name, ns));