//
#if NET_2_0
using System;
-using System.Collections;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
{
KnownTypeCollection types;
IDataContractSurrogate surrogate;
- DataContractResolver resolver; // new in 4.0.
+ DataContractResolver resolver, default_resolver; // new in 4.0.
// 3.5 SP1 supports deserialization by reference (id->obj).
// Though unlike XmlSerializer, it does not support forward-
// reference resolution i.e. a referenced object must appear
// before any references to it.
- Hashtable references = new Hashtable ();
+ Dictionary<string,object> references = new Dictionary<string,object> ();
+ Dictionary<QName,Type> resolved_qnames = new Dictionary<QName,Type> ();
- public static object Deserialize (XmlReader reader, Type type,
- KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, DataContractResolver resolver,
+ public static object Deserialize (XmlReader reader, Type declaredType,
+ KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, DataContractResolver resolver, DataContractResolver defaultResolver,
string name, string ns, bool verifyObjectName)
{
reader.MoveToContent ();
reader.LocalName != name ||
reader.NamespaceURI != ns)
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));
-// Verify (knownTypes, type, name, ns, reader);
- return new XmlFormatterDeserializer (knownTypes, surrogate, resolver).Deserialize (type, reader);
+// Verify (knownTypes, declaredType, name, ns, reader);
+ return new XmlFormatterDeserializer (knownTypes, surrogate, resolver, defaultResolver).Deserialize (declaredType, reader);
}
// Verify the top element name and namespace.
private static void Verify (KnownTypeCollection knownTypes, Type type, string name, string Namespace, XmlReader reader)
{
- QName graph_qname = new QName (reader.Name, reader.NamespaceURI);
+ QName graph_qname = new QName (reader.LocalName, reader.NamespaceURI);
if (graph_qname.Name == name && graph_qname.Namespace == Namespace)
return;
private XmlFormatterDeserializer (
KnownTypeCollection knownTypes,
IDataContractSurrogate surrogate,
- DataContractResolver resolver)
+ DataContractResolver resolver,
+ DataContractResolver defaultResolver)
{
this.types = knownTypes;
this.surrogate = surrogate;
this.resolver = resolver;
+ this.default_resolver = defaultResolver;
}
- public Hashtable References {
+ public Dictionary<string,object> References {
get { return references; }
}
+ XmlDocument document;
+
+ XmlDocument XmlDocument {
+ get { return (document = document ?? new XmlDocument ()); }
+ }
+
// This method handles z:Ref, xsi:nil and primitive types, and then delegates to DeserializeByMap() for anything else.
+
public object Deserialize (Type type, XmlReader reader)
{
- QName graph_qname = types.GetQName (type);
+ if (type == typeof (XmlElement))
+ return XmlDocument.ReadNode (reader);
+ else if (type == typeof (XmlNode [])) {
+ reader.ReadStartElement ();
+ var l = new List<XmlNode> ();
+ for(; !reader.EOF && reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ())
+ l.Add (XmlDocument.ReadNode (reader));
+ reader.ReadEndElement ();
+ return l.ToArray ();
+ }
+ QName graph_qname = null;
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>)) {
+ Type internal_type = type.GetGenericArguments () [0];
+
+ if (types.FindUserMap(internal_type) != null) {
+ graph_qname = types.GetQName (internal_type);
+ }
+ }
+
+ if (graph_qname == null)
+ graph_qname = types.GetQName (type);
+
string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
if (itype != null) {
- string[] parts = itype.Split (':');
+ string [] parts = itype.Split (':');
if (parts.Length > 1)
- graph_qname = new QName (parts [1], reader.LookupNamespace (reader.NameTable.Get (parts[0])));
+ graph_qname = new QName (parts [1], reader.LookupNamespace (reader.NameTable.Get (parts [0])));
else
- graph_qname = new QName (itype, reader.NamespaceURI);
+ graph_qname = new QName (itype, reader.LookupNamespace (String.Empty));
}
string label = reader.GetAttribute ("Ref", KnownTypeCollection.MSSimpleNamespace);
if (label != null) {
- object o = references [label];
- if (o == null)
+ object o;
+ if (!references.TryGetValue (label, out o))
throw new SerializationException (String.Format ("Deserialized object with reference Id '{0}' was not found", label));
reader.Skip ();
return o;
if (isNil) {
reader.Skip ();
- if (!type.IsValueType)
+ if (!type.IsValueType || type == typeof (void))
return null;
else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
return null;
throw new SerializationException (String.Format ("Value type {0} cannot be null.", type));
}
- if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname.Name) != null) {
+ if (resolver != null) {
+ Type t;
+ if (resolved_qnames.TryGetValue (graph_qname, out t))
+ type = t;
+ else { // i.e. resolve name only once.
+ type = resolver.ResolveName (graph_qname.Name, graph_qname.Namespace, type, default_resolver) ?? type;
+ resolved_qnames.Add (graph_qname, type);
+ types.Add (type);
+ }
+ }
+
+ if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname) != null) {
string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
- string value;
- if (reader.IsEmptyElement) {
- reader.Read (); // advance
- if (type.IsValueType)
- return Activator.CreateInstance (type);
- else
- // FIXME: Workaround for creating empty objects of the correct type.
- value = String.Empty;
- }
- else
- value = reader.ReadElementContentAsString ();
- object ret = KnownTypeCollection.PredefinedTypeStringToObject (value, graph_qname.Name, reader);
+ object ret = DeserializePrimitive (type, reader, graph_qname);
+
if (id != null) {
if (references.ContainsKey (id))
throw new InvalidOperationException (String.Format ("Object with Id '{0}' already exists as '{1}'", id, references [id]));
return DeserializeByMap (graph_qname, type, reader);
}
+ object DeserializePrimitive (Type type, XmlReader reader, QName qname)
+ {
+ bool isDateTimeOffset = false;
+ // Handle DateTimeOffset type and DateTimeOffset?.
+ if (type == typeof (DateTimeOffset))
+ isDateTimeOffset = true;
+ else if(type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
+ isDateTimeOffset = type.GetGenericArguments () [0] == typeof (DateTimeOffset);
+ // It is the only exceptional type that does not serialize to string but serializes into complex element.
+ if (isDateTimeOffset) {
+ if (reader.IsEmptyElement) {
+ reader.Read ();
+ return default (DateTimeOffset);
+ }
+ reader.ReadStartElement ();
+ reader.MoveToContent ();
+ var date = reader.ReadElementContentAsDateTime ("DateTime", KnownTypeCollection.DefaultClrNamespaceSystem);
+ var off = TimeSpan.FromMinutes (reader.ReadElementContentAsInt ("OffsetMinutes", KnownTypeCollection.DefaultClrNamespaceSystem));
+ reader.MoveToContent ();
+ reader.ReadEndElement ();
+ return new DateTimeOffset (DateTime.SpecifyKind (date.ToUniversalTime () + off, DateTimeKind.Unspecified), off);
+ }
+
+ string value;
+ if (reader.IsEmptyElement) {
+ reader.Read (); // advance
+ if (type.IsValueType)
+ return Activator.CreateInstance (type);
+ else
+ // FIXME: Workaround for creating empty objects of the correct type.
+ value = String.Empty;
+ }
+ else
+ value = reader.ReadElementContentAsString ();
+ return KnownTypeCollection.PredefinedTypeStringToObject (value, qname.Name, reader);
+ }
+
object DeserializeByMap (QName name, Type type, XmlReader reader)
{
- SerializationMap map = types.FindUserMap (name);
+ SerializationMap map = null;
+ // List<T> and T[] have the same QName, use type to find map work better.
+ if(name.Name.StartsWith ("ArrayOf", StringComparison.Ordinal) || resolved_qnames.ContainsKey (name))
+ map = types.FindUserMap (type);
+ else
+ 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).
if (map == null && (name.Name.StartsWith ("ArrayOf", StringComparison.Ordinal) ||
name.Namespace == KnownTypeCollection.MSArraysNamespace ||
name.Namespace.StartsWith (KnownTypeCollection.DefaultClrNamespaceBase, StringComparison.Ordinal))) {
var it = GetTypeFromNamePair (name.Name, name.Namespace);
- types.TryRegister (it);
+ types.Add (it);
map = types.FindUserMap (name);
}
if (map == null)
Type GetTypeFromNamePair (string name, string ns)
{
- Type p = KnownTypeCollection.GetPrimitiveTypeFromName (name); // FIXME: namespace?
+ Type p = KnownTypeCollection.GetPrimitiveTypeFromName (new QName (name, ns));
if (p != null)
return p;
bool makeArray = false;
if (name.StartsWith ("ArrayOf", StringComparison.Ordinal)) {
name = name.Substring (7); // strip "ArrayOf"
if (ns == KnownTypeCollection.MSArraysNamespace)
- return GetTypeFromNamePair (name, String.Empty).MakeArrayType ();
+ return GetTypeFromNamePair (name, KnownTypeCollection.MSSimpleNamespace).MakeArrayType ();
makeArray = true;
}
foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
Type [] types;
-#if MOONLIGHT
- try {
- types = ass.GetTypes ();
- } catch (System.Reflection.ReflectionTypeLoadException rtle) {
- types = rtle.Types;
- }
-#else
types = ass.GetTypes ();
-#endif
if (types == null)
continue;