[wcf] serialization member namespace was wrong when root namesp ace is given. Fixed...
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / XmlFormatterDeserializer.cs
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.
27 //
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;
37
38 using QName = System.Xml.XmlQualifiedName;
39
40 namespace System.Runtime.Serialization
41 {
42         internal class XmlFormatterDeserializer
43         {
44                 KnownTypeCollection types;
45                 IDataContractSurrogate surrogate;
46                 DataContractResolver 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                 Hashtable references = new Hashtable ();
52
53                 public static object Deserialize (XmlReader reader, Type type,
54                         KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, DataContractResolver resolver,
55                         string name, string ns, bool verifyObjectName)
56                 {
57                         reader.MoveToContent ();
58                         if (verifyObjectName)
59                                 if (reader.NodeType != XmlNodeType.Element ||
60                                     reader.LocalName != name ||
61                                     reader.NamespaceURI != ns)
62                                         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));
63 //                              Verify (knownTypes, type, name, ns, reader);
64                         return new XmlFormatterDeserializer (knownTypes, surrogate, resolver).Deserialize (type, reader);
65                 }
66
67                 // Verify the top element name and namespace.
68                 private static void Verify (KnownTypeCollection knownTypes, Type type, string name, string Namespace, XmlReader reader)
69                 {
70                         QName graph_qname = new QName (reader.Name, reader.NamespaceURI);
71                         if (graph_qname.Name == name && graph_qname.Namespace == Namespace)
72                                 return;
73
74                         // <BClass .. i:type="EClass" >..</BClass>
75                         // Expecting type EClass : allowed
76                         // See test Serialize1b, and Serialize1c (for
77                         // negative cases)
78
79                         // Run through inheritance heirarchy .. 
80                         for (Type baseType = type; baseType != null; baseType = baseType.BaseType)
81                                 if (knownTypes.GetQName (baseType) == graph_qname)
82                                         return;
83
84                         QName typeQName = knownTypes.GetQName (type);
85                         throw new SerializationException (String.Format (
86                                 "Expecting element '{0}' from namespace '{1}'. Encountered 'Element' with name '{2}', namespace '{3}'",
87                                 typeQName.Name, typeQName.Namespace, graph_qname.Name, graph_qname.Namespace));
88                 }
89
90                 private XmlFormatterDeserializer (
91                         KnownTypeCollection knownTypes,
92                         IDataContractSurrogate surrogate,
93                         DataContractResolver resolver)
94                 {
95                         this.types = knownTypes;
96                         this.surrogate = surrogate;
97                         this.resolver = resolver;
98                 }
99
100                 public Hashtable References {
101                         get { return references; }
102                 }
103
104                 // This method handles z:Ref, xsi:nil and primitive types, and then delegates to DeserializeByMap() for anything else.
105                 public object Deserialize (Type type, XmlReader reader)
106                 {
107                         QName graph_qname = types.GetQName (type);
108                         string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
109                         if (itype != null) {
110                                 string[] parts = itype.Split (':');
111                                 if (parts.Length > 1)
112                                         graph_qname = new QName (parts [1], reader.LookupNamespace (reader.NameTable.Get (parts[0])));
113                                 else
114                                         graph_qname = new QName (itype, reader.NamespaceURI);
115                         }
116
117                         string label = reader.GetAttribute ("Ref", KnownTypeCollection.MSSimpleNamespace);
118                         if (label != null) {
119                                 object o = references [label];
120                                 if (o == null)
121                                         throw new SerializationException (String.Format ("Deserialized object with reference Id '{0}' was not found", label));
122                                 reader.Skip ();
123                                 return o;
124                         }
125
126                         bool isNil = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace) == "true";
127
128                         if (isNil) {
129                                 reader.Skip ();
130                                 if (!type.IsValueType)
131                                         return null;
132                                 else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
133                                         return null;
134                                 else 
135                                         throw new SerializationException (String.Format ("Value type {0} cannot be null.", type));
136                         }
137
138                         if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname.Name) != null) {
139                                 string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
140
141                                 string value;
142                                 if (reader.IsEmptyElement) {
143                                         reader.Read (); // advance
144                                         if (type.IsValueType)
145                                                 return Activator.CreateInstance (type);
146                                         else
147                                                 // FIXME: Workaround for creating empty objects of the correct type.
148                                                 value = String.Empty;
149                                 }
150                                 else
151                                         value = reader.ReadElementContentAsString ();
152                                 object ret = KnownTypeCollection.PredefinedTypeStringToObject (value, graph_qname.Name, reader);
153                                 if (id != null) {
154                                         if (references.ContainsKey (id))
155                                                 throw new InvalidOperationException (String.Format ("Object with Id '{0}' already exists as '{1}'", id, references [id]));
156                                         references.Add (id, ret);
157                                 }
158                                 return ret;
159                         }
160
161                         return DeserializeByMap (graph_qname, type, reader);
162                 }
163
164                 object DeserializeByMap (QName name, Type type, XmlReader reader)
165                 {
166                         SerializationMap map = types.FindUserMap (name);
167                         if (map == null && (name.Name.StartsWith ("ArrayOf", StringComparison.Ordinal) ||
168                             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);
173                         }
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));
176
177                         return map.DeserializeObject (reader, this);
178                 }
179
180                 Type GetTypeFromNamePair (string name, string ns)
181                 {
182                         Type p = KnownTypeCollection.GetPrimitiveTypeFromName (name); // FIXME: namespace?
183                         if (p != null)
184                                 return p;
185                         bool makeArray = false;
186                         if (name.StartsWith ("ArrayOf", StringComparison.Ordinal)) {
187                                 name = name.Substring (7); // strip "ArrayOf"
188                                 if (ns == KnownTypeCollection.MSArraysNamespace)
189                                         return GetTypeFromNamePair (name, String.Empty).MakeArrayType ();
190                                 makeArray = true;
191                         }
192
193                         string dnsb = KnownTypeCollection.DefaultClrNamespaceBase;
194                         string clrns = ns.StartsWith (dnsb, StringComparison.Ordinal) ?  ns.Substring (dnsb.Length) : ns;
195
196                         foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
197                                 Type [] types;
198
199 #if MOONLIGHT
200                                 try  {
201                                         types = ass.GetTypes ();
202                                 } catch (System.Reflection.ReflectionTypeLoadException rtle) {
203                                         types = rtle.Types;
204                                 }
205 #else
206                                 types = ass.GetTypes ();
207 #endif
208                                 if (types == null)
209                                         continue;
210
211                                 foreach (var t in types) {
212                                         // there can be null entries or exception throw to access the attribute - 
213                                         // at least when some referenced assemblies could not be loaded (affects moonlight)
214                                         if (t == null)
215                                                 continue;
216
217                                         try {
218                                                 var dca = t.GetCustomAttribute<DataContractAttribute> (true);
219                                                 if (dca != null && dca.Name == name && dca.Namespace == ns)
220                                                         return makeArray ? t.MakeArrayType () : t;
221                                         }
222                                         catch (TypeLoadException tle) {
223                                                 Console.Error.WriteLine (tle);
224                                                 continue;
225                                         }
226                                         catch (FileNotFoundException fnfe) {
227                                                 Console.Error.WriteLine (fnfe);
228                                                 continue;
229                                         }
230
231                                         if (clrns != null && t.Name == name && t.Namespace == clrns)
232                                                 return makeArray ? t.MakeArrayType () : t;
233                                 }
234                         }
235                         throw new XmlException (String.Format ("Type not found; name: {0}, namespace: {1}", name, ns));
236                 }
237         }
238 }
239 #endif