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 bool Generated;
72 public Type ReaderType;
73 public MethodInfo ReaderMethod;
74 public Type WriterType;
75 public MethodInfo WriterMethod;
76 public GenerationBatch Batch;
77 public XmlSerializerImplementation Implementation = null;
79 public XmlSerializationReader CreateReader () {
80 if (ReaderType != null)
81 return (XmlSerializationReader) Activator.CreateInstance (ReaderType);
82 else if (Implementation != null)
83 return Implementation.Reader;
88 public XmlSerializationWriter CreateWriter () {
89 if (WriterType != null)
90 return (XmlSerializationWriter) Activator.CreateInstance (WriterType);
91 else if (Implementation != null)
92 return Implementation.Writer;
98 internal class GenerationBatch
101 public XmlMapping[] Maps;
102 public SerializerData[] Datas;
105 static XmlSerializer ()
107 // The following options are available:
108 // MONO_XMLSERIALIZER_DEBUG: when set to something != "no", it will
109 // it will print the name of the generated file, and it won't
111 // MONO_XMLSERIALIZER_THS: The code generator threshold. It can be:
112 // no: does not use the generator, always the interpreter.
113 // 0: always use the generator, wait until the generation is done.
114 // any number: use the interpreted serializer until the specified
115 // number of serializations is reached. At this point the generation
116 // of the serializer will start in the background. The interpreter
117 // will be used while the serializer is being generated.
119 // XmlSerializer will fall back to the interpreted serializer if
120 // the code generation somehow fails. This can be avoided for
121 // debugging pourposes by adding the "nofallback" option.
122 // For example: MONO_XMLSERIALIZER_THS=0,nofallback
127 generationThreshold = -1;
128 backgroundGeneration = false;
130 string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
131 string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
134 generationThreshold = 50;
135 backgroundGeneration = true;
137 int i = th.IndexOf (',');
139 if (th.Substring (i+1) == "nofallback")
140 generatorFallback = false;
141 th = th.Substring (0, i);
144 if (th.ToLower(CultureInfo.InvariantCulture) == "no")
145 generationThreshold = -1;
147 generationThreshold = int.Parse (th, CultureInfo.InvariantCulture);
148 backgroundGeneration = (generationThreshold != 0);
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;
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,
213 XmlRootAttribute root,
214 string defaultNamespace)
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; }
238 public XmlSerializer (Type type,
239 XmlAttributeOverrides overrides,
241 XmlRootAttribute root,
242 string defaultNamespace,
249 #endregion // Constructors
252 private UnreferencedObjectEventHandler onUnreferencedObject;
253 private XmlAttributeEventHandler onUnknownAttribute;
254 private XmlElementEventHandler onUnknownElement;
255 private XmlNodeEventHandler onUnknownNode;
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; }
273 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e)
275 if (onUnknownAttribute != null) onUnknownAttribute(this, e);
278 internal virtual void OnUnknownElement (XmlElementEventArgs e)
280 if (onUnknownElement != null) onUnknownElement(this, e);
283 internal virtual void OnUnknownNode (XmlNodeEventArgs e)
285 if (onUnknownNode != null) onUnknownNode(this, e);
288 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e)
290 if (onUnreferencedObject != null) onUnreferencedObject(this, e);
293 public event UnreferencedObjectEventHandler UnreferencedObject
295 add { onUnreferencedObject += value; } remove { onUnreferencedObject -= value; }
302 public virtual bool CanDeserialize (XmlReader xmlReader)
304 xmlReader.MoveToContent ();
305 if (typeMapping is XmlMembersMapping)
308 return ((XmlTypeMapping)typeMapping).ElementName == xmlReader.LocalName;
311 protected virtual XmlSerializationReader CreateReader ()
313 // Must be implemented in derived class
314 throw new NotImplementedException ();
317 protected virtual XmlSerializationWriter CreateWriter ()
319 // Must be implemented in derived class
320 throw new NotImplementedException ();
323 public object Deserialize (Stream stream)
325 XmlTextReader xmlReader = new XmlTextReader(stream);
326 xmlReader.Normalization = true;
327 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
328 return Deserialize(xmlReader);
331 public object Deserialize (TextReader textReader)
333 XmlTextReader xmlReader = new XmlTextReader(textReader);
334 xmlReader.Normalization = true;
335 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
336 return Deserialize(xmlReader);
339 public object Deserialize (XmlReader xmlReader)
341 XmlSerializationReader xsReader;
342 if (customSerializer)
343 xsReader = CreateReader ();
345 xsReader = CreateReader (typeMapping);
347 xsReader.Initialize (xmlReader, this);
348 return Deserialize (xsReader);
351 protected virtual object Deserialize (XmlSerializationReader reader)
353 if (customSerializer)
354 // Must be implemented in derived class
355 throw new NotImplementedException ();
358 if (reader is XmlSerializationReaderInterpreter)
359 return ((XmlSerializationReaderInterpreter) reader).ReadRoot ();
362 return serializerData.ReaderMethod.Invoke (reader, null);
363 } catch (TargetInvocationException ex) {
364 throw ex.InnerException;
367 } catch (Exception ex) {
368 if (ex is InvalidOperationException || ex is InvalidCastException)
369 throw new InvalidOperationException ("There is an error in"
370 + " XML document.", ex);
375 public static XmlSerializer [] FromMappings (XmlMapping [] mappings)
377 XmlSerializer[] sers = new XmlSerializer [mappings.Length];
378 SerializerData[] datas = new SerializerData [mappings.Length];
379 GenerationBatch batch = new GenerationBatch ();
380 batch.Maps = mappings;
383 for (int n=0; n<mappings.Length; n++)
385 if (mappings[n] != null)
387 SerializerData data = new SerializerData ();
389 sers[n] = new XmlSerializer (mappings[n], data);
397 public static XmlSerializer [] FromTypes (Type [] types)
399 XmlSerializer [] sers = new XmlSerializer [types.Length];
400 for (int n=0; n<types.Length; n++)
401 sers[n] = new XmlSerializer (types[n]);
405 protected virtual void Serialize (object o, XmlSerializationWriter writer)
407 if (customSerializer)
408 // Must be implemented in derived class
409 throw new NotImplementedException ();
411 if (writer is XmlSerializationWriterInterpreter)
412 ((XmlSerializationWriterInterpreter)writer).WriteRoot (o);
415 serializerData.WriterMethod.Invoke (writer, new object[] {o});
416 } catch (TargetInvocationException ex) {
417 throw ex.InnerException;
422 static Encoding DefaultEncoding = Encoding.Default;
424 public void Serialize (Stream stream, object o)
426 XmlTextWriter xmlWriter = new XmlTextWriter (stream, DefaultEncoding);
427 xmlWriter.Formatting = Formatting.Indented;
428 Serialize (xmlWriter, o, null);
431 public void Serialize (TextWriter textWriter, object o)
433 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
434 xmlWriter.Formatting = Formatting.Indented;
435 Serialize (xmlWriter, o, null);
438 public void Serialize (XmlWriter xmlWriter, object o)
440 Serialize (xmlWriter, o, null);
443 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
445 XmlTextWriter xmlWriter = new XmlTextWriter (stream, DefaultEncoding);
446 xmlWriter.Formatting = Formatting.Indented;
447 Serialize (xmlWriter, o, namespaces);
450 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
452 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
453 xmlWriter.Formatting = Formatting.Indented;
454 Serialize (xmlWriter, o, namespaces);
458 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces)
460 XmlSerializationWriter xsWriter;
463 if (customSerializer)
464 xsWriter = CreateWriter ();
466 xsWriter = CreateWriter (typeMapping);
468 if (namespaces == null || namespaces.Count == 0) {
469 namespaces = new XmlSerializerNamespaces ();
471 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
472 namespaces.Add ("xsd", XmlSchema.Namespace);
474 namespaces.Add ("xsd", XmlSchema.Namespace);
475 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
479 xsWriter.Initialize (xmlWriter, namespaces);
480 Serialize (o, xsWriter);
482 } catch (Exception ex) {
483 if (ex is TargetInvocationException)
484 ex = ex.InnerException;
486 if (ex is InvalidOperationException || ex is InvalidCastException)
487 throw new InvalidOperationException ("There was an error generating" +
488 " the XML document.", ex);
495 public object Deserialize (XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
497 throw new NotImplementedException ();
501 public object Deserialize (XmlReader xmlReader, string encodingStyle)
503 throw new NotImplementedException ();
507 public object Deserialize (XmlReader xmlReader, XmlDeserializationEvents events)
509 throw new NotImplementedException ();
513 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Evidence evidence)
515 throw new NotImplementedException ();
519 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Type type)
521 throw new NotImplementedException ();
525 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings)
527 return GenerateSerializer (types, mappings, null);
531 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings, CompilerParameters parameters)
533 GenerationBatch batch = new GenerationBatch ();
534 batch.Maps = mappings;
535 batch.Datas = new SerializerData [mappings.Length];
537 for (int n=0; n<mappings.Length; n++) {
538 SerializerData data = new SerializerData ();
540 batch.Datas [n] = data;
543 return GenerateSerializers (batch, parameters);
547 public static string GetXmlSerializerAssemblyName (Type type)
549 return type.Assembly.GetName().Name + ".XmlSerializers";
552 public static string GetXmlSerializerAssemblyName (Type type, string defaultNamespace)
554 return GetXmlSerializerAssemblyName (type) + "." + defaultNamespace.GetHashCode ();
558 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces, string encodingStyle)
560 throw new NotImplementedException ();
563 [MonoNotSupported("")]
564 public void Serialize (XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, string encodingStyle, string id)
566 throw new NotImplementedException ();
569 XmlSerializationWriter CreateWriter (XmlMapping typeMapping)
571 XmlSerializationWriter writer;
574 if (serializerData != null) {
575 lock (serializerData) {
576 writer = serializerData.CreateWriter ();
578 if (writer != null) return writer;
583 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
584 return new XmlSerializationWriterInterpreter (typeMapping);
586 CheckGeneratedTypes (typeMapping);
589 lock (serializerData) {
590 writer = serializerData.CreateWriter ();
592 if (writer != null) return writer;
593 if (!generatorFallback)
594 throw new InvalidOperationException ("Error while generating serializer");
598 return new XmlSerializationWriterInterpreter (typeMapping);
601 XmlSerializationReader CreateReader (XmlMapping typeMapping)
604 XmlSerializationReader reader;
607 if (serializerData != null) {
608 lock (serializerData) {
609 reader = serializerData.CreateReader ();
611 if (reader != null) return reader;
615 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
616 return new XmlSerializationReaderInterpreter (typeMapping);
618 CheckGeneratedTypes (typeMapping);
621 lock (serializerData) {
622 reader = serializerData.CreateReader ();
624 if (reader != null) return reader;
625 if (!generatorFallback)
626 throw new InvalidOperationException ("Error while generating serializer");
630 return new XmlSerializationReaderInterpreter (typeMapping);
634 void CheckGeneratedTypes (XmlMapping typeMapping)
636 throw new NotImplementedException();
638 void GenerateSerializersAsync (GenerationBatch batch)
640 throw new NotImplementedException();
642 void RunSerializerGeneration (object obj)
644 throw new NotImplementedException();
647 void CheckGeneratedTypes (XmlMapping typeMapping)
651 if (serializerData == null)
653 lock (serializerTypes)
655 serializerData = (SerializerData) serializerTypes [typeMapping.Source];
656 if (serializerData == null) {
657 serializerData = new SerializerData();
658 serializerTypes [typeMapping.Source] = serializerData;
664 bool generate = false;
665 lock (serializerData)
667 if (serializerData.UsageCount >= generationThreshold && !serializerData.Generated)
668 serializerData.Generated = generate = true;
670 serializerData.UsageCount++;
675 if (serializerData.Batch != null)
676 GenerateSerializersAsync (serializerData.Batch);
679 GenerationBatch batch = new GenerationBatch ();
680 batch.Maps = new XmlMapping[] {typeMapping};
681 batch.Datas = new SerializerData[] {serializerData};
682 GenerateSerializersAsync (batch);
687 void GenerateSerializersAsync (GenerationBatch batch)
689 if (batch.Maps.Length != batch.Datas.Length)
690 throw new ArgumentException ("batch");
694 if (batch.Done) return;
698 if (backgroundGeneration)
699 ThreadPool.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration), batch);
701 RunSerializerGeneration (batch);
704 void RunSerializerGeneration (object obj)
708 GenerationBatch batch = (GenerationBatch) obj;
709 batch = LoadFromSatelliteAssembly (batch);
712 GenerateSerializers (batch, null);
716 Console.WriteLine (ex);
720 static Assembly GenerateSerializers (GenerationBatch batch, CompilerParameters cp)
722 DateTime tim = DateTime.Now;
724 XmlMapping[] maps = batch.Maps;
727 cp = new CompilerParameters();
728 cp.IncludeDebugInformation = false;
729 cp.GenerateInMemory = true;
730 cp.TempFiles.KeepFiles = !deleteTempFiles;
733 string file = cp.TempFiles.AddExtension ("cs");
734 StreamWriter sw = new StreamWriter (file);
736 if (!deleteTempFiles)
737 Console.WriteLine ("Generating " + file);
739 SerializationCodeGenerator gen = new SerializationCodeGenerator (maps);
743 gen.GenerateSerializers (sw);
747 Console.WriteLine ("Serializer could not be generated");
748 Console.WriteLine (ex);
749 cp.TempFiles.Delete ();
754 CSharpCodeProvider provider = new CSharpCodeProvider();
755 ICodeCompiler comp = provider.CreateCompiler ();
757 cp.GenerateExecutable = false;
759 foreach (Type rtype in gen.ReferencedTypes)
761 string path = new Uri (rtype.Assembly.CodeBase).LocalPath;
762 if (!cp.ReferencedAssemblies.Contains (path))
763 cp.ReferencedAssemblies.Add (path);
766 if (!cp.ReferencedAssemblies.Contains ("System.dll"))
767 cp.ReferencedAssemblies.Add ("System.dll");
768 if (!cp.ReferencedAssemblies.Contains ("System.Xml"))
769 cp.ReferencedAssemblies.Add ("System.Xml");
770 if (!cp.ReferencedAssemblies.Contains ("System.Data"))
771 cp.ReferencedAssemblies.Add ("System.Data");
773 CompilerResults res = comp.CompileAssemblyFromFile (cp, file);
774 if (res.Errors.HasErrors || res.CompiledAssembly == null) {
775 Console.WriteLine ("Error while compiling generated serializer");
776 foreach (CompilerError error in res.Errors)
777 Console.WriteLine (error);
779 cp.TempFiles.Delete ();
783 GenerationResult[] results = gen.GenerationResults;
784 for (int n=0; n<results.Length; n++)
786 GenerationResult gres = results[n];
787 SerializerData sd = batch.Datas [n];
790 sd.WriterType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.WriterClassName);
791 sd.ReaderType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.ReaderClassName);
792 sd.WriterMethod = sd.WriterType.GetMethod (gres.WriteMethodName);
793 sd.ReaderMethod = sd.ReaderType.GetMethod (gres.ReadMethodName);
798 cp.TempFiles.Delete ();
800 if (!deleteTempFiles)
801 Console.WriteLine ("Generation finished - " + (DateTime.Now - tim).TotalMilliseconds + " ms");
803 return res.CompiledAssembly;
808 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
813 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
819 #endregion // Methods