**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / System.Xml.Serialization / XmlSerializer.cs
blobdd30cbbb8f957a37ae0c8ff5fcb4613abc57d52a
1 //
2 // XmlSerializer.cs:
3 //
4 // Author:
5 // Lluis Sanchez Gual (lluis@ximian.com)
6 //
7 // (C) 2002, 2003 Ximian, Inc. http://www.ximian.com
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System;
32 using System.Threading;
33 using System.Collections;
34 using System.Globalization;
35 using System.IO;
36 using System.Reflection;
37 using System.Xml;
38 using System.Xml.Schema;
39 using System.Text;
40 using System.CodeDom;
41 using System.CodeDom.Compiler;
42 using Microsoft.CSharp;
43 using System.Configuration;
44 using System.Security.Policy;
46 namespace System.Xml.Serialization
49 public class XmlSerializer
51 internal const string WsdlNamespace = "http://schemas.xmlsoap.org/wsdl/";
52 internal const string EncodingNamespace = "http://schemas.xmlsoap.org/soap/encoding/";
53 static int generationThreshold;
54 static bool backgroundGeneration = true;
55 static bool deleteTempFiles = true;
57 bool customSerializer;
58 XmlMapping typeMapping;
60 SerializerData serializerData;
61 Type writerType;
62 Type readerType;
64 static Hashtable serializerTypes = new Hashtable ();
66 internal class SerializerData
68 public int UsageCount;
69 public Type ReaderType;
70 public MethodInfo ReaderMethod;
71 public Type WriterType;
72 public MethodInfo WriterMethod;
73 public GenerationBatch Batch;
74 public IXmlSerializerImplementation Implementation;
76 public XmlSerializationReader CreateReader () {
77 if (ReaderType != null)
78 return (XmlSerializationReader) Activator.CreateInstance (ReaderType);
79 else if (Implementation != null)
80 return Implementation.Reader;
81 else
82 return null;
85 public XmlSerializationWriter CreateWriter () {
86 if (WriterType != null)
87 return (XmlSerializationWriter) Activator.CreateInstance (WriterType);
88 else if (Implementation != null)
89 return Implementation.Writer;
90 else
91 return null;
95 internal class GenerationBatch
97 public bool Done;
98 public XmlMapping[] Maps;
99 public SerializerData[] Datas;
102 static XmlSerializer ()
104 string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
105 deleteTempFiles = (db == null || db == "no");
107 IDictionary table = (IDictionary) ConfigurationSettings.GetConfig("system.diagnostics");
108 if (table != null) {
109 table = (IDictionary) table["switches"];
110 if (table != null) {
111 string val = (string) table ["XmlSerialization.Compilation"];
112 if (val == "1") deleteTempFiles = false;
116 string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
118 if (th == null) {
119 generationThreshold = 50;
120 backgroundGeneration = true;
122 else if (th.ToLower(CultureInfo.InvariantCulture) == "no")
123 generationThreshold = -1;
124 else {
125 generationThreshold = int.Parse (th, CultureInfo.InvariantCulture);
126 backgroundGeneration = (generationThreshold != 0);
127 if (generationThreshold < 1) generationThreshold = 1;
131 #region Constructors
133 protected XmlSerializer ()
135 customSerializer = true;
138 public XmlSerializer (Type type)
139 : this (type, null, null, null, null)
143 public XmlSerializer (XmlTypeMapping xmlTypeMapping)
145 typeMapping = xmlTypeMapping;
148 internal XmlSerializer (XmlMapping mapping, SerializerData data)
150 typeMapping = mapping;
151 serializerData = data;
154 public XmlSerializer (Type type, string defaultNamespace)
155 : this (type, null, null, null, defaultNamespace)
159 public XmlSerializer (Type type, Type[] extraTypes)
160 : this (type, null, extraTypes, null, null)
164 public XmlSerializer (Type type, XmlAttributeOverrides overrides)
165 : this (type, overrides, null, null, null)
169 public XmlSerializer (Type type, XmlRootAttribute root)
170 : this (type, null, null, root, null)
174 public XmlSerializer (Type type,
175 XmlAttributeOverrides overrides,
176 Type [] extraTypes,
177 XmlRootAttribute root,
178 string defaultNamespace)
180 if (type == null)
181 throw new ArgumentNullException ("type");
183 XmlReflectionImporter importer = new XmlReflectionImporter (overrides, defaultNamespace);
185 if (extraTypes != null)
187 foreach (Type intype in extraTypes)
188 importer.IncludeType (intype);
191 typeMapping = importer.ImportTypeMapping (type, root, defaultNamespace);
194 internal XmlMapping Mapping
196 get { return typeMapping; }
199 #if NET_2_0
201 [MonoTODO]
202 public XmlSerializer (Type type,
203 XmlAttributeOverrides overrides,
204 Type [] extraTypes,
205 XmlRootAttribute root,
206 string defaultNamespace,
207 string location,
208 Evidence evidence)
211 #endif
213 #endregion // Constructors
215 #region Events
217 private XmlAttributeEventHandler onUnknownAttribute;
218 private XmlElementEventHandler onUnknownElement;
219 private XmlNodeEventHandler onUnknownNode;
220 private UnreferencedObjectEventHandler onUnreferencedObject;
222 public event XmlAttributeEventHandler UnknownAttribute
224 add { onUnknownAttribute += value; } remove { onUnknownAttribute -= value; }
227 public event XmlElementEventHandler UnknownElement
229 add { onUnknownElement += value; } remove { onUnknownElement -= value; }
232 public event XmlNodeEventHandler UnknownNode
234 add { onUnknownNode += value; } remove { onUnknownNode -= value; }
237 public event UnreferencedObjectEventHandler UnreferencedObject
239 add { onUnreferencedObject += value; } remove { onUnreferencedObject -= value; }
243 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e)
245 if (onUnknownAttribute != null) onUnknownAttribute(this, e);
248 internal virtual void OnUnknownElement (XmlElementEventArgs e)
250 if (onUnknownElement != null) onUnknownElement(this, e);
253 internal virtual void OnUnknownNode (XmlNodeEventArgs e)
255 if (onUnknownNode != null) onUnknownNode(this, e);
258 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e)
260 if (onUnreferencedObject != null) onUnreferencedObject(this, e);
264 #endregion // Events
266 #region Methods
268 public virtual bool CanDeserialize (XmlReader xmlReader)
270 xmlReader.MoveToContent ();
271 if (typeMapping is XmlMembersMapping)
272 return true;
273 else
274 return ((XmlTypeMapping)typeMapping).ElementName == xmlReader.LocalName;
277 protected virtual XmlSerializationReader CreateReader ()
279 // Must be implemented in derived class
280 throw new NotImplementedException ();
283 protected virtual XmlSerializationWriter CreateWriter ()
285 // Must be implemented in derived class
286 throw new NotImplementedException ();
289 public object Deserialize (Stream stream)
291 XmlTextReader xmlReader = new XmlTextReader(stream);
292 xmlReader.Normalization = true;
293 return Deserialize(xmlReader);
296 public object Deserialize (TextReader textReader)
298 XmlTextReader xmlReader = new XmlTextReader(textReader);
299 xmlReader.Normalization = true;
300 return Deserialize(xmlReader);
303 public object Deserialize (XmlReader xmlReader)
305 XmlSerializationReader xsReader;
306 if (customSerializer)
307 xsReader = CreateReader ();
308 else
309 xsReader = CreateReader (typeMapping);
311 xsReader.Initialize (xmlReader, this);
312 return Deserialize (xsReader);
315 protected virtual object Deserialize (XmlSerializationReader reader)
317 if (customSerializer)
318 // Must be implemented in derived class
319 throw new NotImplementedException ();
321 if (reader is XmlSerializationReaderInterpreter)
322 return ((XmlSerializationReaderInterpreter)reader).ReadRoot ();
323 else
324 return serializerData.ReaderMethod.Invoke (reader, null);
327 public static XmlSerializer [] FromMappings (XmlMapping [] mappings)
329 XmlSerializer[] sers = new XmlSerializer [mappings.Length];
330 SerializerData[] datas = new SerializerData [mappings.Length];
331 GenerationBatch batch = new GenerationBatch ();
332 batch.Maps = mappings;
333 batch.Datas = datas;
335 for (int n=0; n<mappings.Length; n++)
337 if (mappings[n] != null)
339 SerializerData data = new SerializerData ();
340 data.Batch = batch;
341 sers[n] = new XmlSerializer (mappings[n], data);
342 datas[n] = data;
346 return sers;
349 public static XmlSerializer [] FromTypes (Type [] mappings)
351 XmlSerializer [] sers = new XmlSerializer [mappings.Length];
352 for (int n=0; n<mappings.Length; n++)
353 sers[n] = new XmlSerializer (mappings[n]);
354 return sers;
357 protected virtual void Serialize (object o, XmlSerializationWriter writer)
359 if (customSerializer)
360 // Must be implemented in derived class
361 throw new NotImplementedException ();
363 if (writer is XmlSerializationWriterInterpreter)
364 ((XmlSerializationWriterInterpreter)writer).WriteRoot (o);
365 else
366 serializerData.WriterMethod.Invoke (writer, new object[] {o});
369 public void Serialize (Stream stream, object o)
371 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
372 xmlWriter.Formatting = Formatting.Indented;
373 Serialize (xmlWriter, o, null);
376 public void Serialize (TextWriter textWriter, object o)
378 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
379 xmlWriter.Formatting = Formatting.Indented;
380 Serialize (xmlWriter, o, null);
383 public void Serialize (XmlWriter xmlWriter, object o)
385 Serialize (xmlWriter, o, null);
388 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
390 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
391 xmlWriter.Formatting = Formatting.Indented;
392 Serialize (xmlWriter, o, namespaces);
395 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
397 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
398 xmlWriter.Formatting = Formatting.Indented;
399 Serialize (xmlWriter, o, namespaces);
400 xmlWriter.Flush();
403 public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
405 XmlSerializationWriter xsWriter;
407 if (customSerializer)
408 xsWriter = CreateWriter ();
409 else
410 xsWriter = CreateWriter (typeMapping);
412 if (namespaces == null || namespaces.Count == 0)
414 namespaces = new XmlSerializerNamespaces ();
415 namespaces.Add ("xsd", XmlSchema.Namespace);
416 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
419 xsWriter.Initialize (writer, namespaces);
420 Serialize (o, xsWriter);
421 writer.Flush ();
424 #if NET_2_0
426 [MonoTODO]
427 public object Deserialize (XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
429 throw new NotImplementedException ();
432 [MonoTODO]
433 public object Deserialize (XmlReader xmlReader, string encodingStyle)
435 throw new NotImplementedException ();
438 [MonoTODO]
439 public object Deserialize (XmlReader xmlReader, XmlDeserializationEvents events)
441 throw new NotImplementedException ();
444 [MonoTODO]
445 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Evidence evidence)
447 throw new NotImplementedException ();
450 [MonoTODO]
451 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Type type)
453 throw new NotImplementedException ();
456 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings)
458 return GenerateSerializer (types, mappings, null);
461 [MonoTODO]
462 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings, CompilerParameters parameters)
464 GenerationBatch batch = new GenerationBatch ();
465 batch.Maps = mappings;
466 batch.Datas = new SerializerData [mappings.Length];
468 for (int n=0; n<mappings.Length; n++) {
469 SerializerData data = new SerializerData ();
470 data.Batch = batch;
471 batch.Datas [n] = data;
474 return GenerateSerializers (batch, parameters);
477 [MonoTODO]
478 [Obsolete]
479 public static Assembly GenerateSerializer (Type[] types,
480 XmlMapping[] mappings,
481 string codePath,
482 bool debug,
483 bool keepFiles)
485 throw new NotImplementedException ();
488 [MonoTODO]
489 [Obsolete]
490 public static Assembly GenerateSerializer (Type[] types,
491 XmlMapping[] mappings,
492 string codePath,
493 bool debug,
494 bool keepFiles,
495 string compilerOptions)
497 throw new NotImplementedException ();
500 public static string GetXmlSerializerAssemblyName (Type type)
502 return type.Assembly.GetName().Name + ".XmlSerializers";
505 public static string GetXmlSerializerAssemblyName (Type type, string defaultNamespace)
507 return GetXmlSerializerAssemblyName (type) + "." + defaultNamespace.GetHashCode ();
510 [MonoTODO]
511 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces, string encodingStyle)
513 throw new NotImplementedException ();
516 #endif
518 XmlSerializationWriter CreateWriter (XmlMapping typeMapping)
520 XmlSerializationWriter writer;
522 lock (this) {
523 if (serializerData != null) {
524 writer = serializerData.CreateWriter ();
525 if (writer != null) return writer;
529 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
530 return new XmlSerializationWriterInterpreter (typeMapping);
532 CheckGeneratedTypes (typeMapping);
534 lock (this) {
535 writer = serializerData.CreateWriter ();
536 if (writer != null) return writer;
539 return new XmlSerializationWriterInterpreter (typeMapping);
542 XmlSerializationReader CreateReader (XmlMapping typeMapping)
544 XmlSerializationReader reader;
546 lock (this) {
547 if (serializerData != null) {
548 reader = serializerData.CreateReader ();
549 if (reader != null) return reader;
553 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
554 return new XmlSerializationReaderInterpreter (typeMapping);
556 CheckGeneratedTypes (typeMapping);
558 lock (this) {
559 reader = serializerData.CreateReader ();
560 if (reader != null) return reader;
563 return new XmlSerializationReaderInterpreter (typeMapping);
566 void CheckGeneratedTypes (XmlMapping typeMapping)
568 lock (this)
570 if (serializerData == null)
572 lock (serializerTypes)
574 serializerData = (SerializerData) serializerTypes [typeMapping.Source];
575 if (serializerData == null) {
576 serializerData = new SerializerData();
577 serializerTypes [typeMapping.Source] = serializerData;
583 bool generate = false;
584 lock (serializerData)
586 generate = (++serializerData.UsageCount == generationThreshold);
589 if (generate)
591 if (serializerData.Batch != null)
592 GenerateSerializersAsync (serializerData.Batch);
593 else
595 GenerationBatch batch = new GenerationBatch ();
596 batch.Maps = new XmlMapping[] {typeMapping};
597 batch.Datas = new SerializerData[] {serializerData};
598 GenerateSerializersAsync (batch);
603 void GenerateSerializersAsync (GenerationBatch batch)
605 if (batch.Maps.Length != batch.Datas.Length)
606 throw new ArgumentException ("batch");
608 lock (batch)
610 if (batch.Done) return;
611 batch.Done = true;
614 if (backgroundGeneration)
615 ThreadPool.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration), batch);
616 else
617 RunSerializerGeneration (batch);
620 void RunSerializerGeneration (object obj)
624 GenerationBatch batch = (GenerationBatch) obj;
625 batch = LoadFromSatelliteAssembly (batch);
627 if (batch != null)
628 GenerateSerializers (batch, null);
630 catch (Exception ex)
632 Console.WriteLine (ex);
636 static Assembly GenerateSerializers (GenerationBatch batch, CompilerParameters cp)
638 DateTime tim = DateTime.Now;
640 XmlMapping[] maps = batch.Maps;
642 if (cp == null) {
643 cp = new CompilerParameters();
644 cp.IncludeDebugInformation = false;
645 cp.GenerateInMemory = true;
646 cp.TempFiles.KeepFiles = !deleteTempFiles;
649 string file = cp.TempFiles.AddExtension ("cs");
650 StreamWriter sw = new StreamWriter (file);
652 if (!deleteTempFiles)
653 Console.WriteLine ("Generating " + file);
655 SerializationCodeGenerator gen = new SerializationCodeGenerator (maps);
659 gen.GenerateSerializers (sw);
661 catch (Exception ex)
663 Console.WriteLine ("Serializer could not be generated");
664 Console.WriteLine (ex);
665 cp.TempFiles.Delete ();
666 return null;
668 sw.Close ();
670 CSharpCodeProvider provider = new CSharpCodeProvider();
671 ICodeCompiler comp = provider.CreateCompiler ();
673 cp.GenerateExecutable = false;
675 foreach (Type rtype in gen.ReferencedTypes)
677 if (!cp.ReferencedAssemblies.Contains (rtype.Assembly.Location))
678 cp.ReferencedAssemblies.Add (rtype.Assembly.Location);
681 if (!cp.ReferencedAssemblies.Contains ("System.dll"))
682 cp.ReferencedAssemblies.Add ("System.dll");
683 if (!cp.ReferencedAssemblies.Contains ("System.Xml"))
684 cp.ReferencedAssemblies.Add ("System.Xml");
685 if (!cp.ReferencedAssemblies.Contains ("System.Data"))
686 cp.ReferencedAssemblies.Add ("System.Data");
688 CompilerResults res = comp.CompileAssemblyFromFile (cp, file);
689 if (res.Errors.Count > 0)
691 Console.WriteLine ("Error while compiling generated serializer");
692 foreach (CompilerError error in res.Errors)
693 Console.WriteLine (error);
695 cp.TempFiles.Delete ();
696 return null;
699 GenerationResult[] results = gen.GenerationResults;
700 for (int n=0; n<results.Length; n++)
702 GenerationResult gres = results[n];
703 SerializerData sd = batch.Datas [n];
704 lock (sd)
706 sd.WriterType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.WriterClassName);
707 sd.ReaderType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.ReaderClassName);
708 sd.WriterMethod = sd.WriterType.GetMethod (gres.WriteMethodName);
709 sd.ReaderMethod = sd.ReaderType.GetMethod (gres.ReadMethodName);
710 sd.Batch = null;
714 cp.TempFiles.Delete ();
716 if (!deleteTempFiles)
717 Console.WriteLine ("Generation finished - " + (DateTime.Now - tim).TotalMilliseconds + " ms");
719 return res.CompiledAssembly;
722 #if NET_2_0
723 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
725 return batch;
727 #else
728 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
730 return batch;
732 #endif
734 #endregion // Methods