5 // Lluis Sanchez Gual (lluis@ximian.com)
7 // (C) 2002, 2003 Ximian, Inc. http://www.ximian.com
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
32 using System.Threading;
33 using System.Collections;
34 using System.Globalization;
36 using System.Reflection;
38 using System.Xml.Schema;
42 using System.CodeDom.Compiler;
43 using Microsoft.CSharp;
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;
87 public XmlSerializationWriter CreateWriter () {
88 if (WriterType != null)
89 return (XmlSerializationWriter) Activator.CreateInstance (WriterType);
90 else if (Implementation != null)
91 return Implementation.Writer;
97 internal class GenerationBatch
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
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
126 generationThreshold = -1;
127 backgroundGeneration = false;
129 string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
130 string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
133 generationThreshold = 50;
134 backgroundGeneration = true;
136 int i = th.IndexOf (',');
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;
146 generationThreshold = int.Parse (th, CultureInfo.InvariantCulture);
147 backgroundGeneration = (generationThreshold != 0);
148 if (generationThreshold < 1) generationThreshold = 1;
152 deleteTempFiles = (db == null || db == "no");
154 IDictionary table = (IDictionary) ConfigurationSettings.GetConfig("system.diagnostics");
157 table = (IDictionary) table["switches"];
160 string val = (string) table ["XmlSerialization.Compilation"];
161 if (val == "1") deleteTempFiles = false;
168 protected XmlSerializer ()
170 customSerializer = true;
173 public XmlSerializer (Type type)
174 : this (type, null, null, null, null)
178 public XmlSerializer (XmlTypeMapping xmlTypeMapping)
180 typeMapping = xmlTypeMapping;
183 internal XmlSerializer (XmlMapping mapping, SerializerData data)
185 typeMapping = mapping;
186 serializerData = data;
189 public XmlSerializer (Type type, string defaultNamespace)
190 : this (type, null, null, null, defaultNamespace)
194 public XmlSerializer (Type type, Type[] extraTypes)
195 : this (type, null, extraTypes, null, null)
199 public XmlSerializer (Type type, XmlAttributeOverrides overrides)
200 : this (type, overrides, null, null, null)
204 public XmlSerializer (Type type, XmlRootAttribute root)
205 : this (type, null, null, root, null)
209 public XmlSerializer (Type type,
210 XmlAttributeOverrides overrides,
212 XmlRootAttribute root,
213 string defaultNamespace)
216 throw new ArgumentNullException ("type");
218 XmlReflectionImporter importer = new XmlReflectionImporter (overrides, defaultNamespace);
220 if (extraTypes != null)
222 foreach (Type intype in extraTypes)
223 importer.IncludeType (intype);
226 typeMapping = importer.ImportTypeMapping (type, root, defaultNamespace);
229 internal XmlMapping Mapping
231 get { return typeMapping; }
237 public XmlSerializer (Type type,
238 XmlAttributeOverrides overrides,
240 XmlRootAttribute root,
241 string defaultNamespace,
248 #endregion // Constructors
252 private XmlAttributeEventHandler onUnknownAttribute;
253 private XmlElementEventHandler onUnknownElement;
254 private XmlNodeEventHandler onUnknownNode;
255 private UnreferencedObjectEventHandler onUnreferencedObject;
257 public event XmlAttributeEventHandler UnknownAttribute
259 add { onUnknownAttribute += value; } remove { onUnknownAttribute -= value; }
262 public event XmlElementEventHandler UnknownElement
264 add { onUnknownElement += value; } remove { onUnknownElement -= value; }
267 public event XmlNodeEventHandler UnknownNode
269 add { onUnknownNode += value; } remove { onUnknownNode -= value; }
272 public event UnreferencedObjectEventHandler UnreferencedObject
274 add { onUnreferencedObject += value; } remove { onUnreferencedObject -= value; }
278 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e)
280 if (onUnknownAttribute != null) onUnknownAttribute(this, e);
283 internal virtual void OnUnknownElement (XmlElementEventArgs e)
285 if (onUnknownElement != null) onUnknownElement(this, e);
288 internal virtual void OnUnknownNode (XmlNodeEventArgs e)
290 if (onUnknownNode != null) onUnknownNode(this, e);
293 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e)
295 if (onUnreferencedObject != null) onUnreferencedObject(this, e);
303 public virtual bool CanDeserialize (XmlReader xmlReader)
305 xmlReader.MoveToContent ();
306 if (typeMapping is XmlMembersMapping)
309 return ((XmlTypeMapping)typeMapping).ElementName == xmlReader.LocalName;
312 protected virtual XmlSerializationReader CreateReader ()
314 // Must be implemented in derived class
315 throw new NotImplementedException ();
318 protected virtual XmlSerializationWriter CreateWriter ()
320 // Must be implemented in derived class
321 throw new NotImplementedException ();
324 public object Deserialize (Stream stream)
327 var s = new XmlReaderSettings () { IgnoreWhitespace = true };
328 return Deserialize (XmlReader.Create (stream, s));
330 XmlTextReader xmlReader = new XmlTextReader(stream);
331 xmlReader.Normalization = true;
332 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
333 return Deserialize(xmlReader);
337 public object Deserialize (TextReader textReader)
340 var s = new XmlReaderSettings () { IgnoreWhitespace = true };
341 return Deserialize (XmlReader.Create (textReader, s));
343 XmlTextReader xmlReader = new XmlTextReader(textReader);
344 xmlReader.Normalization = true;
345 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
346 return Deserialize(xmlReader);
350 public object Deserialize (XmlReader xmlReader)
352 XmlSerializationReader xsReader;
353 if (customSerializer)
354 xsReader = CreateReader ();
356 xsReader = CreateReader (typeMapping);
358 xsReader.Initialize (xmlReader, this);
359 return Deserialize (xsReader);
362 protected virtual object Deserialize (XmlSerializationReader reader)
364 if (customSerializer)
365 // Must be implemented in derived class
366 throw new NotImplementedException ();
369 if (reader is XmlSerializationReaderInterpreter)
370 return ((XmlSerializationReaderInterpreter) reader).ReadRoot ();
372 return serializerData.ReaderMethod.Invoke (reader, null);
373 } catch (Exception ex) {
374 if (ex is InvalidOperationException || ex is InvalidCastException)
375 throw new InvalidOperationException ("There is an error in"
376 + " XML document.", ex);
381 public static XmlSerializer [] FromMappings (XmlMapping [] mappings)
383 XmlSerializer[] sers = new XmlSerializer [mappings.Length];
384 SerializerData[] datas = new SerializerData [mappings.Length];
385 GenerationBatch batch = new GenerationBatch ();
386 batch.Maps = mappings;
389 for (int n=0; n<mappings.Length; n++)
391 if (mappings[n] != null)
393 SerializerData data = new SerializerData ();
395 sers[n] = new XmlSerializer (mappings[n], data);
403 public static XmlSerializer [] FromTypes (Type [] mappings)
405 XmlSerializer [] sers = new XmlSerializer [mappings.Length];
406 for (int n=0; n<mappings.Length; n++)
407 sers[n] = new XmlSerializer (mappings[n]);
411 protected virtual void Serialize (object o, XmlSerializationWriter writer)
413 if (customSerializer)
414 // Must be implemented in derived class
415 throw new NotImplementedException ();
417 if (writer is XmlSerializationWriterInterpreter)
418 ((XmlSerializationWriterInterpreter)writer).WriteRoot (o);
420 serializerData.WriterMethod.Invoke (writer, new object[] {o});
423 public void Serialize (Stream stream, object o)
425 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
426 xmlWriter.Formatting = Formatting.Indented;
427 Serialize (xmlWriter, o, null);
430 public void Serialize (TextWriter textWriter, object o)
432 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
433 xmlWriter.Formatting = Formatting.Indented;
434 Serialize (xmlWriter, o, null);
437 public void Serialize (XmlWriter xmlWriter, object o)
439 Serialize (xmlWriter, o, null);
442 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
444 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
445 xmlWriter.Formatting = Formatting.Indented;
446 Serialize (xmlWriter, o, namespaces);
449 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
451 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
452 xmlWriter.Formatting = Formatting.Indented;
453 Serialize (xmlWriter, o, namespaces);
457 public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
459 XmlSerializationWriter xsWriter;
462 if (customSerializer)
463 xsWriter = CreateWriter ();
465 xsWriter = CreateWriter (typeMapping);
467 if (namespaces == null || namespaces.Count == 0) {
468 namespaces = new XmlSerializerNamespaces ();
470 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
471 namespaces.Add ("xsd", XmlSchema.Namespace);
473 namespaces.Add ("xsd", XmlSchema.Namespace);
474 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
478 xsWriter.Initialize (writer, namespaces);
479 Serialize (o, xsWriter);
481 } catch (Exception ex) {
482 if (ex is TargetInvocationException)
483 ex = ex.InnerException;
485 if (ex is InvalidOperationException || ex is InvalidCastException)
486 throw new InvalidOperationException ("There was an error generating" +
487 " the XML document.", ex);
496 public object Deserialize (XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
498 throw new NotImplementedException ();
502 public object Deserialize (XmlReader xmlReader, string encodingStyle)
504 throw new NotImplementedException ();
508 public object Deserialize (XmlReader xmlReader, XmlDeserializationEvents events)
510 throw new NotImplementedException ();
514 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Evidence evidence)
516 throw new NotImplementedException ();
520 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Type type)
522 throw new NotImplementedException ();
526 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings)
528 return GenerateSerializer (types, mappings, null);
532 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings, CompilerParameters parameters)
534 GenerationBatch batch = new GenerationBatch ();
535 batch.Maps = mappings;
536 batch.Datas = new SerializerData [mappings.Length];
538 for (int n=0; n<mappings.Length; n++) {
539 SerializerData data = new SerializerData ();
541 batch.Datas [n] = data;
544 return GenerateSerializers (batch, parameters);
548 public static string GetXmlSerializerAssemblyName (Type type)
550 return type.Assembly.GetName().Name + ".XmlSerializers";
553 public static string GetXmlSerializerAssemblyName (Type type, string defaultNamespace)
555 return GetXmlSerializerAssemblyName (type) + "." + defaultNamespace.GetHashCode ();
559 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces, string encodingStyle)
561 throw new NotImplementedException ();
564 [MonoNotSupported("")]
565 public void Serialize (XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, string encodingStyle, string id)
567 throw new NotImplementedException ();
571 XmlSerializationWriter CreateWriter (XmlMapping typeMapping)
573 XmlSerializationWriter writer;
576 if (serializerData != null) {
577 lock (serializerData) {
578 writer = serializerData.CreateWriter ();
580 if (writer != null) return writer;
584 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
585 return new XmlSerializationWriterInterpreter (typeMapping);
587 CheckGeneratedTypes (typeMapping);
590 lock (serializerData) {
591 writer = serializerData.CreateWriter ();
593 if (writer != null) return writer;
594 if (!generatorFallback)
595 throw new InvalidOperationException ("Error while generating serializer");
598 return new XmlSerializationWriterInterpreter (typeMapping);
601 XmlSerializationReader CreateReader (XmlMapping typeMapping)
603 XmlSerializationReader reader;
606 if (serializerData != null) {
607 lock (serializerData) {
608 reader = serializerData.CreateReader ();
610 if (reader != null) return reader;
614 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
615 return new XmlSerializationReaderInterpreter (typeMapping);
617 CheckGeneratedTypes (typeMapping);
620 lock (serializerData) {
621 reader = serializerData.CreateReader ();
623 if (reader != null) return reader;
624 if (!generatorFallback)
625 throw new InvalidOperationException ("Error while generating serializer");
628 return new XmlSerializationReaderInterpreter (typeMapping);
632 void CheckGeneratedTypes (XmlMapping typeMapping)
634 throw new NotImplementedException();
636 void GenerateSerializersAsync (GenerationBatch batch)
638 throw new NotImplementedException();
640 void RunSerializerGeneration (object obj)
642 throw new NotImplementedException();
645 void CheckGeneratedTypes (XmlMapping typeMapping)
649 if (serializerData == null)
651 lock (serializerTypes)
653 serializerData = (SerializerData) serializerTypes [typeMapping.Source];
654 if (serializerData == null) {
655 serializerData = new SerializerData();
656 serializerTypes [typeMapping.Source] = serializerData;
662 bool generate = false;
663 lock (serializerData)
665 generate = (++serializerData.UsageCount == generationThreshold);
670 if (serializerData.Batch != null)
671 GenerateSerializersAsync (serializerData.Batch);
674 GenerationBatch batch = new GenerationBatch ();
675 batch.Maps = new XmlMapping[] {typeMapping};
676 batch.Datas = new SerializerData[] {serializerData};
677 GenerateSerializersAsync (batch);
682 void GenerateSerializersAsync (GenerationBatch batch)
684 if (batch.Maps.Length != batch.Datas.Length)
685 throw new ArgumentException ("batch");
689 if (batch.Done) return;
693 if (backgroundGeneration)
694 ThreadPool.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration), batch);
696 RunSerializerGeneration (batch);
699 void RunSerializerGeneration (object obj)
703 GenerationBatch batch = (GenerationBatch) obj;
704 batch = LoadFromSatelliteAssembly (batch);
707 GenerateSerializers (batch, null);
711 Console.WriteLine (ex);
715 static Assembly GenerateSerializers (GenerationBatch batch, CompilerParameters cp)
717 DateTime tim = DateTime.Now;
719 XmlMapping[] maps = batch.Maps;
722 cp = new CompilerParameters();
723 cp.IncludeDebugInformation = false;
724 cp.GenerateInMemory = true;
725 cp.TempFiles.KeepFiles = !deleteTempFiles;
728 string file = cp.TempFiles.AddExtension ("cs");
729 StreamWriter sw = new StreamWriter (file);
731 if (!deleteTempFiles)
732 Console.WriteLine ("Generating " + file);
734 SerializationCodeGenerator gen = new SerializationCodeGenerator (maps);
738 gen.GenerateSerializers (sw);
742 Console.WriteLine ("Serializer could not be generated");
743 Console.WriteLine (ex);
744 cp.TempFiles.Delete ();
749 CSharpCodeProvider provider = new CSharpCodeProvider();
750 ICodeCompiler comp = provider.CreateCompiler ();
752 cp.GenerateExecutable = false;
754 foreach (Type rtype in gen.ReferencedTypes)
756 if (!cp.ReferencedAssemblies.Contains (rtype.Assembly.Location))
757 cp.ReferencedAssemblies.Add (rtype.Assembly.Location);
760 if (!cp.ReferencedAssemblies.Contains ("System.dll"))
761 cp.ReferencedAssemblies.Add ("System.dll");
762 if (!cp.ReferencedAssemblies.Contains ("System.Xml"))
763 cp.ReferencedAssemblies.Add ("System.Xml");
764 if (!cp.ReferencedAssemblies.Contains ("System.Data"))
765 cp.ReferencedAssemblies.Add ("System.Data");
767 CompilerResults res = comp.CompileAssemblyFromFile (cp, file);
768 if (res.Errors.HasErrors || res.CompiledAssembly == null) {
769 Console.WriteLine ("Error while compiling generated serializer");
770 foreach (CompilerError error in res.Errors)
771 Console.WriteLine (error);
773 cp.TempFiles.Delete ();
777 GenerationResult[] results = gen.GenerationResults;
778 for (int n=0; n<results.Length; n++)
780 GenerationResult gres = results[n];
781 SerializerData sd = batch.Datas [n];
784 sd.WriterType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.WriterClassName);
785 sd.ReaderType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.ReaderClassName);
786 sd.WriterMethod = sd.WriterType.GetMethod (gres.WriteMethodName);
787 sd.ReaderMethod = sd.ReaderType.GetMethod (gres.ReadMethodName);
792 cp.TempFiles.Delete ();
794 if (!deleteTempFiles)
795 Console.WriteLine ("Generation finished - " + (DateTime.Now - tim).TotalMilliseconds + " ms");
797 return res.CompiledAssembly;
802 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
807 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
813 #endregion // Methods