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;
32 using System.Reflection;
33 using System.Runtime.Serialization.Formatters.Binary;
35 using System.Xml.Schema;
37 using QName = System.Xml.XmlQualifiedName;
39 namespace System.Runtime.Serialization
41 internal class XmlFormatterDeserializer
43 KnownTypeCollection types;
44 IDataContractSurrogate surrogate;
45 // 3.5 SP1 supports deserialization by reference (id->obj).
46 // Though unlike XmlSerializer, it does not support forward-
47 // reference resolution i.e. a referenced object must appear
48 // before any references to it.
49 Hashtable references = new Hashtable ();
51 public static object Deserialize (XmlReader reader, Type type,
52 KnownTypeCollection knownTypes, IDataContractSurrogate surrogate,
53 string name, string Namespace, bool verifyObjectName)
55 reader.MoveToContent();
57 Verify (knownTypes, type, name, Namespace, reader);
58 return new XmlFormatterDeserializer (knownTypes, surrogate).Deserialize (type, reader);
61 // Verify the top element name and namespace.
62 private static void Verify (KnownTypeCollection knownTypes, Type type, string name, string Namespace, XmlReader reader)
64 QName graph_qname = new QName (reader.Name, reader.NamespaceURI);
65 if (graph_qname.Name == name && graph_qname.Namespace == Namespace)
68 // <BClass .. i:type="EClass" >..</BClass>
69 // Expecting type EClass : allowed
70 // See test Serialize1b, and Serialize1c (for
73 // Run through inheritance heirarchy ..
74 for (Type baseType = type; baseType != null; baseType = baseType.BaseType)
75 if (knownTypes.GetQName (baseType) == graph_qname)
78 QName typeQName = knownTypes.GetQName (type);
79 throw new SerializationException (String.Format (
80 "Expecting element '{0}' from namespace '{1}'. Encountered 'Element' with name '{2}', namespace '{3}'",
81 typeQName.Name, typeQName.Namespace, graph_qname.Name, graph_qname.Namespace));
84 private XmlFormatterDeserializer (
85 KnownTypeCollection knownTypes,
86 IDataContractSurrogate surrogate)
88 this.types = knownTypes;
89 this.surrogate = surrogate;
92 public Hashtable References {
93 get { return references; }
96 // At the beginning phase, we still have to instantiate a new
97 // target object even if fromContent is true.
98 public object Deserialize (Type type, XmlReader reader)
100 string label = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
101 object o = DeserializeCore (type, reader);
104 references.Add (label, o);
109 public object DeserializeCore (Type type, XmlReader reader)
111 QName graph_qname = types.GetQName (type);
112 string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
114 string[] parts = itype.Split (':');
115 if (parts.Length > 1)
116 graph_qname = new QName (parts [1], reader.LookupNamespace (reader.NameTable.Get (parts[0])));
118 graph_qname = new QName (itype, reader.NamespaceURI);
121 string label = reader.GetAttribute ("Ref", KnownTypeCollection.MSSimpleNamespace);
123 object o = references [label];
125 throw new SerializationException (String.Format ("Deserialized object with reference Id '{0}' was not found", label));
130 bool isNil = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace) == "true";
134 if (!type.IsValueType)
136 else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
139 throw new SerializationException (String.Format ("Value type {0} cannot be null.", type));
142 bool isEmpty = reader.IsEmptyElement;
143 reader.ReadStartElement ();
145 object res = DeserializeContent (graph_qname, type, reader);
147 reader.MoveToContent ();
148 if (reader.NodeType == XmlNodeType.EndElement)
149 reader.ReadEndElement ();
150 else if (!isEmpty && reader.NodeType != XmlNodeType.None)
151 throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.", reader.NodeType, reader.Name, reader.NamespaceURI, type.FullName));
155 object DeserializeContent (QName name, Type type, XmlReader reader)
157 if (KnownTypeCollection.IsPrimitiveType (name)) {
159 if (reader.NodeType != XmlNodeType.Text)
160 if (type.IsValueType)
161 return Activator.CreateInstance (type);
163 // FIXME: Workaround for creating empty objects of the correct type.
164 value = String.Empty;
166 value = reader.ReadContentAsString ();
167 return KnownTypeCollection.PredefinedTypeStringToObject (value, name.Name, reader);
170 SerializationMap map = types.FindUserMap (name);
171 if (map == null && name.Namespace.StartsWith (KnownTypeCollection.DefaultClrNamespaceBase, StringComparison.Ordinal)) {
172 var it = GetTypeFromNamePair (name.Name, name.Namespace.Substring (KnownTypeCollection.DefaultClrNamespaceBase.Length));
173 if (types.TryRegister (it))
174 map = types.FindUserMap (name);
177 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));
179 return map.DeserializeContent (reader, this);
182 Type GetTypeFromNamePair (string name, string ns)
184 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ())
185 foreach (var t in ass.GetTypes ())
186 if (t.Name == name && t.Namespace == ns)