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
;
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 // 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 ();
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
)
73 // <BClass .. i:type="EClass" >..</BClass>
74 // Expecting type EClass : allowed
75 // See test Serialize1b, and Serialize1c (for
78 // Run through inheritance heirarchy ..
79 for (Type baseType
= type
; baseType
!= null; baseType
= baseType
.BaseType
)
80 if (knownTypes
.GetQName (baseType
) == graph_qname
)
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
);
109 references
.Add (label
, o
);
114 public object DeserializeCore (Type type
, XmlReader reader
)
116 QName graph_qname
= types
.GetQName (type
);
117 string itype
= reader
.GetAttribute ("type", XmlSchema
.InstanceNamespace
);
119 string[] parts
= itype
.Split (':');
120 if (parts
.Length
> 1)
121 graph_qname
= new QName (parts
[1], reader
.LookupNamespace (reader
.NameTable
.Get (parts
[0])));
123 graph_qname
= new QName (itype
, reader
.NamespaceURI
);
126 string label
= reader
.GetAttribute ("Ref", KnownTypeCollection
.MSSimpleNamespace
);
128 object o
= references
[label
];
130 throw new SerializationException (String
.Format ("Deserialized object with reference Id '{0}' was not found", label
));
135 bool isNil
= reader
.GetAttribute ("nil", XmlSchema
.InstanceNamespace
) == "true";
139 if (!type
.IsValueType
)
141 else if (type
.IsGenericType
&& type
.GetGenericTypeDefinition () == typeof (Nullable
<>))
144 throw new SerializationException (String
.Format ("Value type {0} cannot be null.", type
));
147 if (KnownTypeCollection
.GetPrimitiveTypeFromName (graph_qname
.Name
) != null) {
149 if (reader
.IsEmptyElement
) {
150 reader
.Read (); // advance
151 if (type
.IsValueType
)
152 return Activator
.CreateInstance (type
);
154 // FIXME: Workaround for creating empty objects of the correct type.
155 value = String
.Empty
;
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
);
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?
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
)
196 if (clrns
!= null && t
.Name
== name
&& t
.Namespace
== clrns
)
200 throw new XmlException (String
.Format ("Type not found; name: {0}, namespace: {1}", name
, ns
));