// // XmlSerializer.cs: // // Author: // Lluis Sanchez Gual (lluis@ximian.com) // // (C) 2002, 2003 Ximian, Inc. http://www.ximian.com // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Threading; using System.Collections; using System.Globalization; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Schema; using System.Text; #if !TARGET_JVM && !NET_2_1 using System.CodeDom; using System.CodeDom.Compiler; using Microsoft.CSharp; #endif using System.Configuration; using System.Security.Policy; namespace System.Xml.Serialization { public class XmlSerializer { internal const string WsdlNamespace = "http://schemas.xmlsoap.org/wsdl/"; internal const string EncodingNamespace = "http://schemas.xmlsoap.org/soap/encoding/"; internal const string WsdlTypesNamespace = "http://microsoft.com/wsdl/types/"; static int generationThreshold; static bool backgroundGeneration = true; static bool deleteTempFiles = true; static bool generatorFallback = true; bool customSerializer; XmlMapping typeMapping; SerializerData serializerData; static Hashtable serializerTypes = new Hashtable (); internal class SerializerData { public int UsageCount; public Type ReaderType; public MethodInfo ReaderMethod; public Type WriterType; public MethodInfo WriterMethod; public GenerationBatch Batch; public XmlSerializerImplementation Implementation = null; public XmlSerializationReader CreateReader () { if (ReaderType != null) return (XmlSerializationReader) Activator.CreateInstance (ReaderType); else if (Implementation != null) return Implementation.Reader; else return null; } public XmlSerializationWriter CreateWriter () { if (WriterType != null) return (XmlSerializationWriter) Activator.CreateInstance (WriterType); else if (Implementation != null) return Implementation.Writer; else return null; } } internal class GenerationBatch { public bool Done; public XmlMapping[] Maps; public SerializerData[] Datas; } static XmlSerializer () { // The following options are available: // MONO_XMLSERIALIZER_DEBUG: when set to something != "no", it will // it will print the name of the generated file, and it won't // be deleted. // MONO_XMLSERIALIZER_THS: The code generator threshold. It can be: // no: does not use the generator, always the interpreter. // 0: always use the generator, wait until the generation is done. // any number: use the interpreted serializer until the specified // number of serializations is reached. At this point the generation // of the serializer will start in the background. The interpreter // will be used while the serializer is being generated. // // XmlSerializer will fall back to the interpreted serializer if // the code generation somehow fails. This can be avoided for // debugging pourposes by adding the "nofallback" option. // For example: MONO_XMLSERIALIZER_THS=0,nofallback #if TARGET_JVM || NET_2_1 string db = null; string th = null; generationThreshold = -1; backgroundGeneration = false; #else string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG"); string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS"); if (th == null) { generationThreshold = 50; backgroundGeneration = true; } else { int i = th.IndexOf (','); if (i != -1) { if (th.Substring (i+1) == "nofallback") generatorFallback = false; th = th.Substring (0, i); } if (th.ToLower(CultureInfo.InvariantCulture) == "no") generationThreshold = -1; else { generationThreshold = int.Parse (th, CultureInfo.InvariantCulture); backgroundGeneration = (generationThreshold != 0); if (generationThreshold < 1) generationThreshold = 1; } } #endif deleteTempFiles = (db == null || db == "no"); #if !NET_2_1 IDictionary table = (IDictionary) ConfigurationSettings.GetConfig("system.diagnostics"); if (table != null) { table = (IDictionary) table["switches"]; if (table != null) { string val = (string) table ["XmlSerialization.Compilation"]; if (val == "1") deleteTempFiles = false; } } #endif } #region Constructors protected XmlSerializer () { customSerializer = true; } public XmlSerializer (Type type) : this (type, null, null, null, null) { } public XmlSerializer (XmlTypeMapping xmlTypeMapping) { typeMapping = xmlTypeMapping; } internal XmlSerializer (XmlMapping mapping, SerializerData data) { typeMapping = mapping; serializerData = data; } public XmlSerializer (Type type, string defaultNamespace) : this (type, null, null, null, defaultNamespace) { } public XmlSerializer (Type type, Type[] extraTypes) : this (type, null, extraTypes, null, null) { } public XmlSerializer (Type type, XmlAttributeOverrides overrides) : this (type, overrides, null, null, null) { } public XmlSerializer (Type type, XmlRootAttribute root) : this (type, null, null, root, null) { } public XmlSerializer (Type type, XmlAttributeOverrides overrides, Type [] extraTypes, XmlRootAttribute root, string defaultNamespace) { if (type == null) throw new ArgumentNullException ("type"); XmlReflectionImporter importer = new XmlReflectionImporter (overrides, defaultNamespace); if (extraTypes != null) { foreach (Type intype in extraTypes) importer.IncludeType (intype); } typeMapping = importer.ImportTypeMapping (type, root, defaultNamespace); } internal XmlMapping Mapping { get { return typeMapping; } } #if NET_2_0 [MonoTODO] public XmlSerializer (Type type, XmlAttributeOverrides overrides, Type [] extraTypes, XmlRootAttribute root, string defaultNamespace, string location, Evidence evidence) { } #endif #endregion // Constructors #region Events private UnreferencedObjectEventHandler onUnreferencedObject; #if !MOONLIGHT private XmlAttributeEventHandler onUnknownAttribute; private XmlElementEventHandler onUnknownElement; private XmlNodeEventHandler onUnknownNode; public event XmlAttributeEventHandler UnknownAttribute { add { onUnknownAttribute += value; } remove { onUnknownAttribute -= value; } } public event XmlElementEventHandler UnknownElement { add { onUnknownElement += value; } remove { onUnknownElement -= value; } } public event XmlNodeEventHandler UnknownNode { add { onUnknownNode += value; } remove { onUnknownNode -= value; } } internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e) { if (onUnknownAttribute != null) onUnknownAttribute(this, e); } internal virtual void OnUnknownElement (XmlElementEventArgs e) { if (onUnknownElement != null) onUnknownElement(this, e); } internal virtual void OnUnknownNode (XmlNodeEventArgs e) { if (onUnknownNode != null) onUnknownNode(this, e); } #endif internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e) { if (onUnreferencedObject != null) onUnreferencedObject(this, e); } public event UnreferencedObjectEventHandler UnreferencedObject { add { onUnreferencedObject += value; } remove { onUnreferencedObject -= value; } } #endregion // Events #region Methods public virtual bool CanDeserialize (XmlReader xmlReader) { xmlReader.MoveToContent (); if (typeMapping is XmlMembersMapping) return true; else return ((XmlTypeMapping)typeMapping).ElementName == xmlReader.LocalName; } protected virtual XmlSerializationReader CreateReader () { // Must be implemented in derived class throw new NotImplementedException (); } protected virtual XmlSerializationWriter CreateWriter () { // Must be implemented in derived class throw new NotImplementedException (); } public object Deserialize (Stream stream) { XmlTextReader xmlReader = new XmlTextReader(stream); xmlReader.Normalization = true; xmlReader.WhitespaceHandling = WhitespaceHandling.Significant; return Deserialize(xmlReader); } public object Deserialize (TextReader textReader) { XmlTextReader xmlReader = new XmlTextReader(textReader); xmlReader.Normalization = true; xmlReader.WhitespaceHandling = WhitespaceHandling.Significant; return Deserialize(xmlReader); } public object Deserialize (XmlReader xmlReader) { XmlSerializationReader xsReader; if (customSerializer) xsReader = CreateReader (); else xsReader = CreateReader (typeMapping); xsReader.Initialize (xmlReader, this); return Deserialize (xsReader); } protected virtual object Deserialize (XmlSerializationReader reader) { if (customSerializer) // Must be implemented in derived class throw new NotImplementedException (); try { if (reader is XmlSerializationReaderInterpreter) return ((XmlSerializationReaderInterpreter) reader).ReadRoot (); else { try { return serializerData.ReaderMethod.Invoke (reader, null); } catch (TargetInvocationException ex) { throw ex.InnerException; } } } catch (Exception ex) { if (ex is InvalidOperationException || ex is InvalidCastException) throw new InvalidOperationException ("There is an error in" + " XML document.", ex); throw; } } public static XmlSerializer [] FromMappings (XmlMapping [] mappings) { XmlSerializer[] sers = new XmlSerializer [mappings.Length]; SerializerData[] datas = new SerializerData [mappings.Length]; GenerationBatch batch = new GenerationBatch (); batch.Maps = mappings; batch.Datas = datas; for (int n=0; n