X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Runtime.Serialization%2FSystem.Runtime.Serialization%2FXmlFormatterDeserializer.cs;h=b4eae19e8d874f080a69aae7dd2fc04ebde95e07;hb=dd5e72a46ff2fb187db84c1392692b0523008b60;hp=e548975f3ff06f3e9c3ca71102d615783393ddca;hpb=f936428455da14ab57f031e167d415673ff76bc6;p=mono.git diff --git a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs index e548975f3ff..b4eae19e8d8 100644 --- a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs +++ b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs @@ -27,8 +27,9 @@ // #if NET_2_0 using System; -using System.Collections; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using System.Xml; @@ -42,26 +43,32 @@ namespace System.Runtime.Serialization { KnownTypeCollection types; IDataContractSurrogate surrogate; + 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 references = new Dictionary (); + Dictionary resolved_qnames = new Dictionary (); - public static object Deserialize (XmlReader reader, Type type, - KnownTypeCollection knownTypes, IDataContractSurrogate surrogate, - string name, string Namespace, bool verifyObjectName) + 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.MoveToContent (); if (verifyObjectName) - Verify (knownTypes, type, name, Namespace, reader); - return new XmlFormatterDeserializer (knownTypes, surrogate).Deserialize (type, reader); + if (reader.NodeType != XmlNodeType.Element || + 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, 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; @@ -83,45 +90,66 @@ namespace System.Runtime.Serialization private XmlFormatterDeserializer ( KnownTypeCollection knownTypes, - IDataContractSurrogate surrogate) + IDataContractSurrogate surrogate, + DataContractResolver resolver, + DataContractResolver defaultResolver) { this.types = knownTypes; this.surrogate = surrogate; + this.resolver = resolver; + this.default_resolver = defaultResolver; } - public Hashtable References { + public Dictionary References { get { return references; } } - // At the beginning phase, we still have to instantiate a new - // target object even if fromContent is true. - public object Deserialize (Type type, XmlReader reader) - { - string label = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace); - object o = DeserializeCore (type, reader); - - if (label != null) - references.Add (label, o); - - return o; + XmlDocument document; + + XmlDocument XmlDocument { + get { return (document = document ?? new XmlDocument ()); } } - public object DeserializeCore (Type type, XmlReader reader) + // 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 (); + 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; @@ -131,7 +159,7 @@ namespace System.Runtime.Serialization 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; @@ -139,53 +167,139 @@ namespace System.Runtime.Serialization throw new SerializationException (String.Format ("Value type {0} cannot be null.", type)); } - bool isEmpty = reader.IsEmptyElement; - reader.ReadStartElement (); + 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); + } + } - object res = DeserializeContent (graph_qname, type, reader); + if (KnownTypeCollection.GetPrimitiveTypeFromName (graph_qname) != null) { + string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace); - reader.MoveToContent (); - if (reader.NodeType == XmlNodeType.EndElement) - reader.ReadEndElement (); - else if (!isEmpty && reader.NodeType != XmlNodeType.None) - 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)); - return res; + 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])); + references.Add (id, ret); + } + return ret; + } + + return DeserializeByMap (graph_qname, type, reader); } - object DeserializeContent (QName name, Type type, XmlReader reader) + object DeserializePrimitive (Type type, XmlReader reader, QName qname) { - if (KnownTypeCollection.IsPrimitiveType (name)) { - string value; - if (reader.NodeType != XmlNodeType.Text) - if (type.IsValueType) - return Activator.CreateInstance (type); - else - // FIXME: Workaround for creating empty objects of the correct type. - value = String.Empty; + 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 - value = reader.ReadContentAsString (); - return KnownTypeCollection.PredefinedTypeStringToObject (value, name.Name, reader); + // FIXME: Workaround for creating empty objects of the correct type. + value = String.Empty; } + else + value = reader.ReadElementContentAsString (); + return KnownTypeCollection.PredefinedTypeStringToObject (value, qname.Name, reader); + } - SerializationMap map = types.FindUserMap (name); - if (map == null && name.Namespace.StartsWith (KnownTypeCollection.DefaultClrNamespaceBase, StringComparison.Ordinal)) { - var it = GetTypeFromNamePair (name.Name, name.Namespace.Substring (KnownTypeCollection.DefaultClrNamespaceBase.Length)); - if (types.TryRegister (it)) - map = types.FindUserMap (name); + object DeserializeByMap (QName name, Type type, XmlReader reader) + { + SerializationMap map = null; + // List 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.Add (it); + map = types.FindUserMap (name); } if (map == null) 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)); - return map.DeserializeContent (reader, this); + return map.DeserializeObject (reader, this); } Type GetTypeFromNamePair (string name, string ns) { - foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) - foreach (var t in ass.GetTypes ()) - if (t.Name == name && t.Namespace == ns) - return t; - return null; + 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 (); + makeArray = true; + } + + string dnsb = KnownTypeCollection.DefaultClrNamespaceBase; + string clrns = ns.StartsWith (dnsb, StringComparison.Ordinal) ? ns.Substring (dnsb.Length) : ns; + + foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) { + Type [] types; + + types = ass.GetTypes (); + if (types == null) + continue; + + foreach (var t in types) { + // there can be null entries or exception throw to access the attribute - + // at least when some referenced assemblies could not be loaded (affects moonlight) + if (t == null) + continue; + + try { + var dca = t.GetCustomAttribute (true); + if (dca != null && dca.Name == name && dca.Namespace == ns) + return makeArray ? t.MakeArrayType () : t; + } + catch (TypeLoadException tle) { + Console.Error.WriteLine (tle); + continue; + } + catch (FileNotFoundException fnfe) { + Console.Error.WriteLine (fnfe); + continue; + } + + if (clrns != null && t.Name == name && t.Namespace == clrns) + return makeArray ? t.MakeArrayType () : t; + } + } + throw new XmlException (String.Format ("Type not found; name: {0}, namespace: {1}", name, ns)); } } }