2010-04-15 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Runtime.Serialization / System.Runtime.Serialization / XmlFormatterDeserializer.cs
blob1659a2463ed871d090a5012a87dfd74f5169ac3f
1 //
2 // XmlFormatterDeserializer.cs
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
8 //
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
28 #if NET_2_0
29 using System;
30 using System.Collections;
31 using System.IO;
32 using System.Linq;
33 using System.Reflection;
34 using System.Runtime.Serialization.Formatters.Binary;
35 using System.Xml;
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 // 3.5 SP1 supports deserialization by reference (id->obj).
47 // Though unlike XmlSerializer, it does not support forward-
48 // reference resolution i.e. a referenced object must appear
49 // before any references to it.
50 Hashtable references = new Hashtable ();
52 public static object Deserialize (XmlReader reader, Type type,
53 KnownTypeCollection knownTypes, IDataContractSurrogate surrogate,
54 string name, string ns, bool verifyObjectName)
56 reader.MoveToContent ();
57 if (verifyObjectName)
58 if (reader.NodeType != XmlNodeType.Element ||
59 reader.LocalName != name ||
60 reader.NamespaceURI != ns)
61 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));
62 // Verify (knownTypes, type, name, ns, reader);
63 return new XmlFormatterDeserializer (knownTypes, surrogate).Deserialize (type, reader);
66 // Verify the top element name and namespace.
67 private static void Verify (KnownTypeCollection knownTypes, Type type, string name, string Namespace, XmlReader reader)
69 QName graph_qname = new QName (reader.Name, reader.NamespaceURI);
70 if (graph_qname.Name == name && graph_qname.Namespace == Namespace)
71 return;
73 // <BClass .. i:type="EClass" >..</BClass>
74 // Expecting type EClass : allowed
75 // See test Serialize1b, and Serialize1c (for
76 // negative cases)
78 // Run through inheritance heirarchy ..
79 for (Type baseType = type; baseType != null; baseType = baseType.BaseType)
80 if (knownTypes.GetQName (baseType) == graph_qname)
81 return;
83 QName typeQName = knownTypes.GetQName (type);
84 throw new SerializationException (String.Format (
85 "Expecting element '{0}' from namespace '{1}'. Encountered 'Element' with name '{2}', namespace '{3}'",
86 typeQName.Name, typeQName.Namespace, graph_qname.Name, graph_qname.Namespace));
89 private XmlFormatterDeserializer (
90 KnownTypeCollection knownTypes,
91 IDataContractSurrogate surrogate)
93 this.types = knownTypes;
94 this.surrogate = surrogate;
97 public Hashtable References {
98 get { return references; }
101 // At the beginning phase, we still have to instantiate a new
102 // target object even if fromContent is true.
103 public object Deserialize (Type type, XmlReader reader)
105 string label = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
106 object o = DeserializeCore (type, reader);
108 if (label != null)
109 references.Add (label, o);
111 return o;
114 public object DeserializeCore (Type type, XmlReader reader)
116 QName graph_qname = types.GetQName (type);
117 string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
118 if (itype != null) {
119 string[] parts = itype.Split (':');
120 if (parts.Length > 1)
121 graph_qname = new QName (parts [1], reader.LookupNamespace (reader.NameTable.Get (parts[0])));
122 else
123 graph_qname = new QName (itype, reader.NamespaceURI);
126 string label = reader.GetAttribute ("Ref", KnownTypeCollection.MSSimpleNamespace);
127 if (label != null) {
128 object o = references [label];
129 if (o == null)
130 throw new SerializationException (String.Format ("Deserialized object with reference Id '{0}' was not found", label));
131 reader.Skip ();
132 return o;
135 bool isNil = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace) == "true";
137 if (isNil) {
138 reader.Skip ();
139 if (!type.IsValueType)
140 return null;
141 else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
142 return null;
143 else
144 throw new SerializationException (String.Format ("Value type {0} cannot be null.", type));
147 if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname.Name) != null) {
148 string value;
149 if (reader.IsEmptyElement) {
150 reader.Read (); // advance
151 if (type.IsValueType)
152 return Activator.CreateInstance (type);
153 else
154 // FIXME: Workaround for creating empty objects of the correct type.
155 value = String.Empty;
157 else
158 value = reader.ReadElementContentAsString ();
159 return KnownTypeCollection.PredefinedTypeStringToObject (value, graph_qname.Name, reader);
162 return DeserializeByMap (graph_qname, type, reader);
165 object DeserializeByMap (QName name, Type type, XmlReader reader)
167 SerializationMap map = types.FindUserMap (name);
168 if (map == null && (name.Namespace == KnownTypeCollection.MSArraysNamespace ||
169 name.Namespace.StartsWith (KnownTypeCollection.DefaultClrNamespaceBase, StringComparison.Ordinal))) {
170 var it = GetTypeFromNamePair (name.Name, name.Namespace);
171 types.TryRegister (it);
172 map = types.FindUserMap (name);
174 if (map == null)
175 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));
177 return map.DeserializeObject (reader, this);
180 Type GetTypeFromNamePair (string name, string ns)
182 Type p = KnownTypeCollection.GetPrimitiveTypeFromName (name); // FIXME: namespace?
183 if (p != null)
184 return p;
185 if (name.StartsWith ("ArrayOf", StringComparison.Ordinal) && ns == KnownTypeCollection.MSArraysNamespace)
186 return GetTypeFromNamePair (name.Substring (7), String.Empty).MakeArrayType ();
188 int xlen = KnownTypeCollection.DefaultClrNamespaceBase.Length;
189 string clrns = ns.Length > xlen ? ns.Substring (xlen) : null;
191 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
192 foreach (var t in ass.GetTypes ()) {
193 var dca = t.GetCustomAttribute<DataContractAttribute> (true);
194 if (dca != null && dca.Name == name && dca.Namespace == ns)
195 return t;
196 if (clrns != null && t.Name == name && t.Namespace == clrns)
197 return t;
200 throw new XmlException (String.Format ("Type not found; name: {0}, namespace: {1}", name, ns));
204 #endif