2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.XML / System.Xml.Serialization / XmlSerializer.cs
blob380774f4be215023671db1580ab55d17e3c0ffbe
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 #if !TARGET_JVM && !MONOTOUCH
41 using System.CodeDom;
42 using System.CodeDom.Compiler;
43 using Microsoft.CSharp;
44 #endif
45 using System.Configuration;
46 using System.Security.Policy;
48 namespace System.Xml.Serialization
51 public class XmlSerializer
53 internal const string WsdlNamespace = "http://schemas.xmlsoap.org/wsdl/";
54 internal const string EncodingNamespace = "http://schemas.xmlsoap.org/soap/encoding/";
55 internal const string WsdlTypesNamespace = "http://microsoft.com/wsdl/types/";
56 static int generationThreshold;
57 static bool backgroundGeneration = true;
58 static bool deleteTempFiles = true;
59 static bool generatorFallback = true;
61 bool customSerializer;
62 XmlMapping typeMapping;
64 SerializerData serializerData;
66 static Hashtable serializerTypes = new Hashtable ();
68 internal class SerializerData
70 public int UsageCount;
71 public Type ReaderType;
72 public MethodInfo ReaderMethod;
73 public Type WriterType;
74 public MethodInfo WriterMethod;
75 public GenerationBatch Batch;
76 public XmlSerializerImplementation Implementation = null;
78 public XmlSerializationReader CreateReader () {
79 if (ReaderType != null)
80 return (XmlSerializationReader) Activator.CreateInstance (ReaderType);
81 else if (Implementation != null)
82 return Implementation.Reader;
83 else
84 return null;
87 public XmlSerializationWriter CreateWriter () {
88 if (WriterType != null)
89 return (XmlSerializationWriter) Activator.CreateInstance (WriterType);
90 else if (Implementation != null)
91 return Implementation.Writer;
92 else
93 return null;
97 internal class GenerationBatch
99 public bool Done;
100 public XmlMapping[] Maps;
101 public SerializerData[] Datas;
104 static XmlSerializer ()
106 // The following options are available:
107 // MONO_XMLSERIALIZER_DEBUG: when set to something != "no", it will
108 // it will print the name of the generated file, and it won't
109 // be deleted.
110 // MONO_XMLSERIALIZER_THS: The code generator threshold. It can be:
111 // no: does not use the generator, always the interpreter.
112 // 0: always use the generator, wait until the generation is done.
113 // any number: use the interpreted serializer until the specified
114 // number of serializations is reached. At this point the generation
115 // of the serializer will start in the background. The interpreter
116 // will be used while the serializer is being generated.
118 // XmlSerializer will fall back to the interpreted serializer if
119 // the code generation somehow fails. This can be avoided for
120 // debugging pourposes by adding the "nofallback" option.
121 // For example: MONO_XMLSERIALIZER_THS=0,nofallback
123 #if TARGET_JVM || MONOTOUCH
124 string db = null;
125 string th = null;
126 generationThreshold = -1;
127 backgroundGeneration = false;
128 #else
129 string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
130 string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
132 if (th == null) {
133 generationThreshold = 50;
134 backgroundGeneration = true;
135 } else {
136 int i = th.IndexOf (',');
137 if (i != -1) {
138 if (th.Substring (i+1) == "nofallback")
139 generatorFallback = false;
140 th = th.Substring (0, i);
143 if (th.ToLower(CultureInfo.InvariantCulture) == "no")
144 generationThreshold = -1;
145 else {
146 generationThreshold = int.Parse (th, CultureInfo.InvariantCulture);
147 backgroundGeneration = (generationThreshold != 0);
148 if (generationThreshold < 1) generationThreshold = 1;
151 #endif
152 deleteTempFiles = (db == null || db == "no");
153 #if !MONOTOUCH
154 IDictionary table = (IDictionary) ConfigurationSettings.GetConfig("system.diagnostics");
155 if (table != null)
157 table = (IDictionary) table["switches"];
158 if (table != null)
160 string val = (string) table ["XmlSerialization.Compilation"];
161 if (val == "1") deleteTempFiles = false;
164 #endif
167 #region Constructors
169 protected XmlSerializer ()
171 customSerializer = true;
174 public XmlSerializer (Type type)
175 : this (type, null, null, null, null)
179 public XmlSerializer (XmlTypeMapping xmlTypeMapping)
181 typeMapping = xmlTypeMapping;
184 internal XmlSerializer (XmlMapping mapping, SerializerData data)
186 typeMapping = mapping;
187 serializerData = data;
190 public XmlSerializer (Type type, string defaultNamespace)
191 : this (type, null, null, null, defaultNamespace)
195 public XmlSerializer (Type type, Type[] extraTypes)
196 : this (type, null, extraTypes, null, null)
200 public XmlSerializer (Type type, XmlAttributeOverrides overrides)
201 : this (type, overrides, null, null, null)
205 public XmlSerializer (Type type, XmlRootAttribute root)
206 : this (type, null, null, root, null)
210 public XmlSerializer (Type type,
211 XmlAttributeOverrides overrides,
212 Type [] extraTypes,
213 XmlRootAttribute root,
214 string defaultNamespace)
216 if (type == null)
217 throw new ArgumentNullException ("type");
219 XmlReflectionImporter importer = new XmlReflectionImporter (overrides, defaultNamespace);
221 if (extraTypes != null)
223 foreach (Type intype in extraTypes)
224 importer.IncludeType (intype);
227 typeMapping = importer.ImportTypeMapping (type, root, defaultNamespace);
230 internal XmlMapping Mapping
232 get { return typeMapping; }
235 #if NET_2_0
237 [MonoTODO]
238 public XmlSerializer (Type type,
239 XmlAttributeOverrides overrides,
240 Type [] extraTypes,
241 XmlRootAttribute root,
242 string defaultNamespace,
243 string location,
244 Evidence evidence)
247 #endif
249 #endregion // Constructors
251 #region Events
253 private XmlAttributeEventHandler onUnknownAttribute;
254 private XmlElementEventHandler onUnknownElement;
255 private XmlNodeEventHandler onUnknownNode;
256 private UnreferencedObjectEventHandler onUnreferencedObject;
258 public event XmlAttributeEventHandler UnknownAttribute
260 add { onUnknownAttribute += value; } remove { onUnknownAttribute -= value; }
263 public event XmlElementEventHandler UnknownElement
265 add { onUnknownElement += value; } remove { onUnknownElement -= value; }
268 public event XmlNodeEventHandler UnknownNode
270 add { onUnknownNode += value; } remove { onUnknownNode -= value; }
273 public event UnreferencedObjectEventHandler UnreferencedObject
275 add { onUnreferencedObject += value; } remove { onUnreferencedObject -= value; }
279 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e)
281 if (onUnknownAttribute != null) onUnknownAttribute(this, e);
284 internal virtual void OnUnknownElement (XmlElementEventArgs e)
286 if (onUnknownElement != null) onUnknownElement(this, e);
289 internal virtual void OnUnknownNode (XmlNodeEventArgs e)
291 if (onUnknownNode != null) onUnknownNode(this, e);
294 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e)
296 if (onUnreferencedObject != null) onUnreferencedObject(this, e);
300 #endregion // Events
302 #region Methods
304 public virtual bool CanDeserialize (XmlReader xmlReader)
306 xmlReader.MoveToContent ();
307 if (typeMapping is XmlMembersMapping)
308 return true;
309 else
310 return ((XmlTypeMapping)typeMapping).ElementName == xmlReader.LocalName;
313 protected virtual XmlSerializationReader CreateReader ()
315 // Must be implemented in derived class
316 throw new NotImplementedException ();
319 protected virtual XmlSerializationWriter CreateWriter ()
321 // Must be implemented in derived class
322 throw new NotImplementedException ();
325 public object Deserialize (Stream stream)
327 XmlTextReader xmlReader = new XmlTextReader(stream);
328 xmlReader.Normalization = true;
329 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
330 return Deserialize(xmlReader);
333 public object Deserialize (TextReader textReader)
335 XmlTextReader xmlReader = new XmlTextReader(textReader);
336 xmlReader.Normalization = true;
337 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
338 return Deserialize(xmlReader);
341 public object Deserialize (XmlReader xmlReader)
343 XmlSerializationReader xsReader;
344 if (customSerializer)
345 xsReader = CreateReader ();
346 else
347 xsReader = CreateReader (typeMapping);
349 xsReader.Initialize (xmlReader, this);
350 return Deserialize (xsReader);
353 protected virtual object Deserialize (XmlSerializationReader reader)
355 if (customSerializer)
356 // Must be implemented in derived class
357 throw new NotImplementedException ();
359 try {
360 if (reader is XmlSerializationReaderInterpreter)
361 return ((XmlSerializationReaderInterpreter) reader).ReadRoot ();
362 else
363 return serializerData.ReaderMethod.Invoke (reader, null);
364 } catch (Exception ex) {
365 if (ex is InvalidOperationException || ex is InvalidCastException)
366 throw new InvalidOperationException ("There is an error in"
367 + " XML document.", ex);
368 throw;
372 public static XmlSerializer [] FromMappings (XmlMapping [] mappings)
374 XmlSerializer[] sers = new XmlSerializer [mappings.Length];
375 SerializerData[] datas = new SerializerData [mappings.Length];
376 GenerationBatch batch = new GenerationBatch ();
377 batch.Maps = mappings;
378 batch.Datas = datas;
380 for (int n=0; n<mappings.Length; n++)
382 if (mappings[n] != null)
384 SerializerData data = new SerializerData ();
385 data.Batch = batch;
386 sers[n] = new XmlSerializer (mappings[n], data);
387 datas[n] = data;
391 return sers;
394 public static XmlSerializer [] FromTypes (Type [] mappings)
396 XmlSerializer [] sers = new XmlSerializer [mappings.Length];
397 for (int n=0; n<mappings.Length; n++)
398 sers[n] = new XmlSerializer (mappings[n]);
399 return sers;
402 protected virtual void Serialize (object o, XmlSerializationWriter writer)
404 if (customSerializer)
405 // Must be implemented in derived class
406 throw new NotImplementedException ();
408 if (writer is XmlSerializationWriterInterpreter)
409 ((XmlSerializationWriterInterpreter)writer).WriteRoot (o);
410 else
411 serializerData.WriterMethod.Invoke (writer, new object[] {o});
414 public void Serialize (Stream stream, object o)
416 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
417 xmlWriter.Formatting = Formatting.Indented;
418 Serialize (xmlWriter, o, null);
421 public void Serialize (TextWriter textWriter, object o)
423 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
424 xmlWriter.Formatting = Formatting.Indented;
425 Serialize (xmlWriter, o, null);
428 public void Serialize (XmlWriter xmlWriter, object o)
430 Serialize (xmlWriter, o, null);
433 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
435 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
436 xmlWriter.Formatting = Formatting.Indented;
437 Serialize (xmlWriter, o, namespaces);
440 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
442 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
443 xmlWriter.Formatting = Formatting.Indented;
444 Serialize (xmlWriter, o, namespaces);
445 xmlWriter.Flush();
448 public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
450 XmlSerializationWriter xsWriter;
452 try {
453 if (customSerializer)
454 xsWriter = CreateWriter ();
455 else
456 xsWriter = CreateWriter (typeMapping);
458 if (namespaces == null || namespaces.Count == 0) {
459 namespaces = new XmlSerializerNamespaces ();
460 #if NET_2_0
461 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
462 namespaces.Add ("xsd", XmlSchema.Namespace);
463 #else
464 namespaces.Add ("xsd", XmlSchema.Namespace);
465 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
466 #endif
469 xsWriter.Initialize (writer, namespaces);
470 Serialize (o, xsWriter);
471 writer.Flush ();
472 } catch (Exception ex) {
473 if (ex is TargetInvocationException)
474 ex = ex.InnerException;
476 if (ex is InvalidOperationException || ex is InvalidCastException)
477 throw new InvalidOperationException ("There was an error generating" +
478 " the XML document.", ex);
480 throw;
484 #if NET_2_0
486 [MonoTODO]
487 public object Deserialize (XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
489 throw new NotImplementedException ();
492 [MonoTODO]
493 public object Deserialize (XmlReader xmlReader, string encodingStyle)
495 throw new NotImplementedException ();
498 [MonoTODO]
499 public object Deserialize (XmlReader xmlReader, XmlDeserializationEvents events)
501 throw new NotImplementedException ();
504 [MonoTODO]
505 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Evidence evidence)
507 throw new NotImplementedException ();
510 [MonoTODO]
511 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Type type)
513 throw new NotImplementedException ();
516 #if !TARGET_JVM && !MONOTOUCH
517 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings)
519 return GenerateSerializer (types, mappings, null);
522 [MonoTODO]
523 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings, CompilerParameters parameters)
525 GenerationBatch batch = new GenerationBatch ();
526 batch.Maps = mappings;
527 batch.Datas = new SerializerData [mappings.Length];
529 for (int n=0; n<mappings.Length; n++) {
530 SerializerData data = new SerializerData ();
531 data.Batch = batch;
532 batch.Datas [n] = data;
535 return GenerateSerializers (batch, parameters);
537 #endif
539 public static string GetXmlSerializerAssemblyName (Type type)
541 return type.Assembly.GetName().Name + ".XmlSerializers";
544 public static string GetXmlSerializerAssemblyName (Type type, string defaultNamespace)
546 return GetXmlSerializerAssemblyName (type) + "." + defaultNamespace.GetHashCode ();
549 [MonoTODO]
550 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces, string encodingStyle)
552 throw new NotImplementedException ();
555 [MonoNotSupported("")]
556 public void Serialize (XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, string encodingStyle, string id)
558 throw new NotImplementedException ();
560 #endif
562 XmlSerializationWriter CreateWriter (XmlMapping typeMapping)
564 XmlSerializationWriter writer;
566 lock (this) {
567 if (serializerData != null) {
568 lock (serializerData) {
569 writer = serializerData.CreateWriter ();
571 if (writer != null) return writer;
575 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
576 return new XmlSerializationWriterInterpreter (typeMapping);
578 CheckGeneratedTypes (typeMapping);
580 lock (this) {
581 lock (serializerData) {
582 writer = serializerData.CreateWriter ();
584 if (writer != null) return writer;
585 if (!generatorFallback)
586 throw new InvalidOperationException ("Error while generating serializer");
589 return new XmlSerializationWriterInterpreter (typeMapping);
592 XmlSerializationReader CreateReader (XmlMapping typeMapping)
594 XmlSerializationReader reader;
596 lock (this) {
597 if (serializerData != null) {
598 lock (serializerData) {
599 reader = serializerData.CreateReader ();
601 if (reader != null) return reader;
605 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
606 return new XmlSerializationReaderInterpreter (typeMapping);
608 CheckGeneratedTypes (typeMapping);
610 lock (this) {
611 lock (serializerData) {
612 reader = serializerData.CreateReader ();
614 if (reader != null) return reader;
615 if (!generatorFallback)
616 throw new InvalidOperationException ("Error while generating serializer");
619 return new XmlSerializationReaderInterpreter (typeMapping);
622 #if TARGET_JVM || MONOTOUCH
623 void CheckGeneratedTypes (XmlMapping typeMapping)
625 throw new NotImplementedException();
627 void GenerateSerializersAsync (GenerationBatch batch)
629 throw new NotImplementedException();
631 void RunSerializerGeneration (object obj)
633 throw new NotImplementedException();
635 #else
636 void CheckGeneratedTypes (XmlMapping typeMapping)
638 lock (this)
640 if (serializerData == null)
642 lock (serializerTypes)
644 serializerData = (SerializerData) serializerTypes [typeMapping.Source];
645 if (serializerData == null) {
646 serializerData = new SerializerData();
647 serializerTypes [typeMapping.Source] = serializerData;
653 bool generate = false;
654 lock (serializerData)
656 generate = (++serializerData.UsageCount == generationThreshold);
659 if (generate)
661 if (serializerData.Batch != null)
662 GenerateSerializersAsync (serializerData.Batch);
663 else
665 GenerationBatch batch = new GenerationBatch ();
666 batch.Maps = new XmlMapping[] {typeMapping};
667 batch.Datas = new SerializerData[] {serializerData};
668 GenerateSerializersAsync (batch);
673 void GenerateSerializersAsync (GenerationBatch batch)
675 if (batch.Maps.Length != batch.Datas.Length)
676 throw new ArgumentException ("batch");
678 lock (batch)
680 if (batch.Done) return;
681 batch.Done = true;
684 if (backgroundGeneration)
685 ThreadPool.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration), batch);
686 else
687 RunSerializerGeneration (batch);
690 void RunSerializerGeneration (object obj)
694 GenerationBatch batch = (GenerationBatch) obj;
695 batch = LoadFromSatelliteAssembly (batch);
697 if (batch != null)
698 GenerateSerializers (batch, null);
700 catch (Exception ex)
702 Console.WriteLine (ex);
706 static Assembly GenerateSerializers (GenerationBatch batch, CompilerParameters cp)
708 DateTime tim = DateTime.Now;
710 XmlMapping[] maps = batch.Maps;
712 if (cp == null) {
713 cp = new CompilerParameters();
714 cp.IncludeDebugInformation = false;
715 cp.GenerateInMemory = true;
716 cp.TempFiles.KeepFiles = !deleteTempFiles;
719 string file = cp.TempFiles.AddExtension ("cs");
720 StreamWriter sw = new StreamWriter (file);
722 if (!deleteTempFiles)
723 Console.WriteLine ("Generating " + file);
725 SerializationCodeGenerator gen = new SerializationCodeGenerator (maps);
729 gen.GenerateSerializers (sw);
731 catch (Exception ex)
733 Console.WriteLine ("Serializer could not be generated");
734 Console.WriteLine (ex);
735 cp.TempFiles.Delete ();
736 return null;
738 sw.Close ();
740 CSharpCodeProvider provider = new CSharpCodeProvider();
741 ICodeCompiler comp = provider.CreateCompiler ();
743 cp.GenerateExecutable = false;
745 foreach (Type rtype in gen.ReferencedTypes)
747 string path = new Uri (rtype.Assembly.CodeBase).LocalPath;
748 if (!cp.ReferencedAssemblies.Contains (path))
749 cp.ReferencedAssemblies.Add (path);
752 if (!cp.ReferencedAssemblies.Contains ("System.dll"))
753 cp.ReferencedAssemblies.Add ("System.dll");
754 if (!cp.ReferencedAssemblies.Contains ("System.Xml"))
755 cp.ReferencedAssemblies.Add ("System.Xml");
756 if (!cp.ReferencedAssemblies.Contains ("System.Data"))
757 cp.ReferencedAssemblies.Add ("System.Data");
759 CompilerResults res = comp.CompileAssemblyFromFile (cp, file);
760 if (res.Errors.HasErrors || res.CompiledAssembly == null) {
761 Console.WriteLine ("Error while compiling generated serializer");
762 foreach (CompilerError error in res.Errors)
763 Console.WriteLine (error);
765 cp.TempFiles.Delete ();
766 return null;
769 GenerationResult[] results = gen.GenerationResults;
770 for (int n=0; n<results.Length; n++)
772 GenerationResult gres = results[n];
773 SerializerData sd = batch.Datas [n];
774 lock (sd)
776 sd.WriterType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.WriterClassName);
777 sd.ReaderType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.ReaderClassName);
778 sd.WriterMethod = sd.WriterType.GetMethod (gres.WriteMethodName);
779 sd.ReaderMethod = sd.ReaderType.GetMethod (gres.ReadMethodName);
780 sd.Batch = null;
784 cp.TempFiles.Delete ();
786 if (!deleteTempFiles)
787 Console.WriteLine ("Generation finished - " + (DateTime.Now - tim).TotalMilliseconds + " ms");
789 return res.CompiledAssembly;
791 #endif
793 #if NET_2_0
794 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
796 return batch;
798 #else
799 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
801 return batch;
803 #endif
805 #endregion // Methods