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 static int generationThreshold;
56 static bool backgroundGeneration = true;
57 static bool deleteTempFiles = true;
58 static bool generatorFallback = true;
60 bool customSerializer;
61 XmlMapping typeMapping;
63 SerializerData serializerData;
65 static Hashtable serializerTypes = new Hashtable ();
67 internal class SerializerData
69 public int UsageCount;
70 public Type ReaderType;
71 public MethodInfo ReaderMethod;
72 public Type WriterType;
73 public MethodInfo WriterMethod;
74 public GenerationBatch Batch;
75 public IXmlSerializerImplementation Implementation = null;
77 public XmlSerializationReader CreateReader () {
78 if (ReaderType != null)
79 return (XmlSerializationReader) Activator.CreateInstance (ReaderType);
80 else if (Implementation != null)
81 return Implementation.Reader;
86 public XmlSerializationWriter CreateWriter () {
87 if (WriterType != null)
88 return (XmlSerializationWriter) Activator.CreateInstance (WriterType);
89 else if (Implementation != null)
90 return Implementation.Writer;
96 internal class GenerationBatch
99 public XmlMapping[] Maps;
100 public SerializerData[] Datas;
103 static XmlSerializer ()
105 // The following options are available:
106 // MONO_XMLSERIALIZER_DEBUG: when set to something != "no", it will
107 // it will print the name of the generated file, and it won't
109 // MONO_XMLSERIALIZER_THS: The code generator threshold. It can be:
110 // no: does not use the generator, always the interpreter.
111 // 0: always use the generator, wait until the generation is done.
112 // any number: use the interpreted serializer until the specified
113 // number of serializations is reached. At this point the generation
114 // of the serializer will start in the background. The interpreter
115 // will be used while the serializer is being generated.
117 // XmlSerializer will fall back to the interpreted serializer if
118 // the code generation somehow fails. This can be avoided for
119 // debugging pourposes by adding the "nofallback" option.
120 // For example: MONO_XMLSERIALIZER_THS=0,nofallback
125 generationThreshold = -1;
126 backgroundGeneration = false;
128 string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
129 string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
132 generationThreshold = 50;
133 backgroundGeneration = true;
135 int i = th.IndexOf (',');
137 if (th.Substring (i+1) == "nofallback")
138 generatorFallback = false;
139 th = th.Substring (0, i);
142 if (th.ToLower(CultureInfo.InvariantCulture) == "no")
143 generationThreshold = -1;
145 generationThreshold = int.Parse (th, CultureInfo.InvariantCulture);
146 backgroundGeneration = (generationThreshold != 0);
147 if (generationThreshold < 1) generationThreshold = 1;
151 deleteTempFiles = (db == null || db == "no");
153 IDictionary table = (IDictionary) ConfigurationSettings.GetConfig("system.diagnostics");
156 table = (IDictionary) table["switches"];
159 string val = (string) table ["XmlSerialization.Compilation"];
160 if (val == "1") deleteTempFiles = false;
167 protected XmlSerializer ()
169 customSerializer = true;
172 public XmlSerializer (Type type)
173 : this (type, null, null, null, null)
177 public XmlSerializer (XmlTypeMapping xmlTypeMapping)
179 typeMapping = xmlTypeMapping;
182 internal XmlSerializer (XmlMapping mapping, SerializerData data)
184 typeMapping = mapping;
185 serializerData = data;
188 public XmlSerializer (Type type, string defaultNamespace)
189 : this (type, null, null, null, defaultNamespace)
193 public XmlSerializer (Type type, Type[] extraTypes)
194 : this (type, null, extraTypes, null, null)
198 public XmlSerializer (Type type, XmlAttributeOverrides overrides)
199 : this (type, overrides, null, null, null)
203 public XmlSerializer (Type type, XmlRootAttribute root)
204 : this (type, null, null, root, null)
208 public XmlSerializer (Type type,
209 XmlAttributeOverrides overrides,
211 XmlRootAttribute root,
212 string defaultNamespace)
215 throw new ArgumentNullException ("type");
217 XmlReflectionImporter importer = new XmlReflectionImporter (overrides, defaultNamespace);
219 if (extraTypes != null)
221 foreach (Type intype in extraTypes)
222 importer.IncludeType (intype);
225 typeMapping = importer.ImportTypeMapping (type, root, defaultNamespace);
228 internal XmlMapping Mapping
230 get { return typeMapping; }
236 public XmlSerializer (Type type,
237 XmlAttributeOverrides overrides,
239 XmlRootAttribute root,
240 string defaultNamespace,
247 #endregion // Constructors
251 private XmlAttributeEventHandler onUnknownAttribute;
252 private XmlElementEventHandler onUnknownElement;
253 private XmlNodeEventHandler onUnknownNode;
254 private UnreferencedObjectEventHandler onUnreferencedObject;
256 public event XmlAttributeEventHandler UnknownAttribute
258 add { onUnknownAttribute += value; } remove { onUnknownAttribute -= value; }
261 public event XmlElementEventHandler UnknownElement
263 add { onUnknownElement += value; } remove { onUnknownElement -= value; }
266 public event XmlNodeEventHandler UnknownNode
268 add { onUnknownNode += value; } remove { onUnknownNode -= value; }
271 public event UnreferencedObjectEventHandler UnreferencedObject
273 add { onUnreferencedObject += value; } remove { onUnreferencedObject -= value; }
277 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e)
279 if (onUnknownAttribute != null) onUnknownAttribute(this, e);
282 internal virtual void OnUnknownElement (XmlElementEventArgs e)
284 if (onUnknownElement != null) onUnknownElement(this, e);
287 internal virtual void OnUnknownNode (XmlNodeEventArgs e)
289 if (onUnknownNode != null) onUnknownNode(this, e);
292 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e)
294 if (onUnreferencedObject != null) onUnreferencedObject(this, e);
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 return Deserialize(xmlReader);
330 public object Deserialize (TextReader textReader)
332 XmlTextReader xmlReader = new XmlTextReader(textReader);
333 xmlReader.Normalization = true;
334 return Deserialize(xmlReader);
337 public object Deserialize (XmlReader xmlReader)
339 XmlSerializationReader xsReader;
340 if (customSerializer)
341 xsReader = CreateReader ();
343 xsReader = CreateReader (typeMapping);
345 xsReader.Initialize (xmlReader, this);
346 return Deserialize (xsReader);
349 protected virtual object Deserialize (XmlSerializationReader reader)
351 if (customSerializer)
352 // Must be implemented in derived class
353 throw new NotImplementedException ();
355 if (reader is XmlSerializationReaderInterpreter)
356 return ((XmlSerializationReaderInterpreter)reader).ReadRoot ();
358 return serializerData.ReaderMethod.Invoke (reader, null);
361 public static XmlSerializer [] FromMappings (XmlMapping [] mappings)
363 XmlSerializer[] sers = new XmlSerializer [mappings.Length];
364 SerializerData[] datas = new SerializerData [mappings.Length];
365 GenerationBatch batch = new GenerationBatch ();
366 batch.Maps = mappings;
369 for (int n=0; n<mappings.Length; n++)
371 if (mappings[n] != null)
373 SerializerData data = new SerializerData ();
375 sers[n] = new XmlSerializer (mappings[n], data);
383 public static XmlSerializer [] FromTypes (Type [] mappings)
385 XmlSerializer [] sers = new XmlSerializer [mappings.Length];
386 for (int n=0; n<mappings.Length; n++)
387 sers[n] = new XmlSerializer (mappings[n]);
391 protected virtual void Serialize (object o, XmlSerializationWriter writer)
393 if (customSerializer)
394 // Must be implemented in derived class
395 throw new NotImplementedException ();
397 if (writer is XmlSerializationWriterInterpreter)
398 ((XmlSerializationWriterInterpreter)writer).WriteRoot (o);
400 serializerData.WriterMethod.Invoke (writer, new object[] {o});
403 public void Serialize (Stream stream, object o)
405 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
406 xmlWriter.Formatting = Formatting.Indented;
407 Serialize (xmlWriter, o, null);
410 public void Serialize (TextWriter textWriter, object o)
412 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
413 xmlWriter.Formatting = Formatting.Indented;
414 Serialize (xmlWriter, o, null);
417 public void Serialize (XmlWriter xmlWriter, object o)
419 Serialize (xmlWriter, o, null);
422 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
424 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
425 xmlWriter.Formatting = Formatting.Indented;
426 Serialize (xmlWriter, o, namespaces);
429 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
431 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
432 xmlWriter.Formatting = Formatting.Indented;
433 Serialize (xmlWriter, o, namespaces);
437 public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
439 XmlSerializationWriter xsWriter;
441 if (customSerializer)
442 xsWriter = CreateWriter ();
444 xsWriter = CreateWriter (typeMapping);
446 if (namespaces == null || namespaces.Count == 0)
448 namespaces = new XmlSerializerNamespaces ();
449 namespaces.Add ("xsd", XmlSchema.Namespace);
450 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
453 xsWriter.Initialize (writer, namespaces);
454 Serialize (o, xsWriter);
461 public object Deserialize (XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
463 throw new NotImplementedException ();
467 public object Deserialize (XmlReader xmlReader, string encodingStyle)
469 throw new NotImplementedException ();
473 public object Deserialize (XmlReader xmlReader, XmlDeserializationEvents events)
475 throw new NotImplementedException ();
479 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Evidence evidence)
481 throw new NotImplementedException ();
485 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Type type)
487 throw new NotImplementedException ();
490 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings)
492 return GenerateSerializer (types, mappings, null);
496 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings, CompilerParameters parameters)
498 GenerationBatch batch = new GenerationBatch ();
499 batch.Maps = mappings;
500 batch.Datas = new SerializerData [mappings.Length];
502 for (int n=0; n<mappings.Length; n++) {
503 SerializerData data = new SerializerData ();
505 batch.Datas [n] = data;
508 return GenerateSerializers (batch, parameters);
513 public static Assembly GenerateSerializer (Type[] types,
514 XmlMapping[] mappings,
519 throw new NotImplementedException ();
524 public static Assembly GenerateSerializer (Type[] types,
525 XmlMapping[] mappings,
529 string compilerOptions)
531 throw new NotImplementedException ();
534 public static string GetXmlSerializerAssemblyName (Type type)
536 return type.Assembly.GetName().Name + ".XmlSerializers";
539 public static string GetXmlSerializerAssemblyName (Type type, string defaultNamespace)
541 return GetXmlSerializerAssemblyName (type) + "." + defaultNamespace.GetHashCode ();
545 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces, string encodingStyle)
547 throw new NotImplementedException ();
552 XmlSerializationWriter CreateWriter (XmlMapping typeMapping)
554 XmlSerializationWriter writer;
557 if (serializerData != null) {
558 lock (serializerData) {
559 writer = serializerData.CreateWriter ();
561 if (writer != null) return writer;
565 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
566 return new XmlSerializationWriterInterpreter (typeMapping);
568 CheckGeneratedTypes (typeMapping);
571 lock (serializerData) {
572 writer = serializerData.CreateWriter ();
574 if (writer != null) return writer;
575 if (!generatorFallback)
576 throw new InvalidOperationException ("Error while generating serializer");
579 return new XmlSerializationWriterInterpreter (typeMapping);
582 XmlSerializationReader CreateReader (XmlMapping typeMapping)
584 XmlSerializationReader reader;
587 if (serializerData != null) {
588 lock (serializerData) {
589 reader = serializerData.CreateReader ();
591 if (reader != null) return reader;
595 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
596 return new XmlSerializationReaderInterpreter (typeMapping);
598 CheckGeneratedTypes (typeMapping);
601 lock (serializerData) {
602 reader = serializerData.CreateReader ();
604 if (reader != null) return reader;
605 if (!generatorFallback)
606 throw new InvalidOperationException ("Error while generating serializer");
609 return new XmlSerializationReaderInterpreter (typeMapping);
613 void CheckGeneratedTypes (XmlMapping typeMapping)
615 throw new NotImplementedException();
617 void GenerateSerializersAsync (GenerationBatch batch)
619 throw new NotImplementedException();
621 void RunSerializerGeneration (object obj)
623 throw new NotImplementedException();
626 void CheckGeneratedTypes (XmlMapping typeMapping)
630 if (serializerData == null)
632 lock (serializerTypes)
634 serializerData = (SerializerData) serializerTypes [typeMapping.Source];
635 if (serializerData == null) {
636 serializerData = new SerializerData();
637 serializerTypes [typeMapping.Source] = serializerData;
643 bool generate = false;
644 lock (serializerData)
646 generate = (++serializerData.UsageCount == generationThreshold);
651 if (serializerData.Batch != null)
652 GenerateSerializersAsync (serializerData.Batch);
655 GenerationBatch batch = new GenerationBatch ();
656 batch.Maps = new XmlMapping[] {typeMapping};
657 batch.Datas = new SerializerData[] {serializerData};
658 GenerateSerializersAsync (batch);
663 void GenerateSerializersAsync (GenerationBatch batch)
665 if (batch.Maps.Length != batch.Datas.Length)
666 throw new ArgumentException ("batch");
670 if (batch.Done) return;
674 if (backgroundGeneration)
675 ThreadPool.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration), batch);
677 RunSerializerGeneration (batch);
680 void RunSerializerGeneration (object obj)
684 GenerationBatch batch = (GenerationBatch) obj;
685 batch = LoadFromSatelliteAssembly (batch);
688 GenerateSerializers (batch, null);
692 Console.WriteLine (ex);
696 static Assembly GenerateSerializers (GenerationBatch batch, CompilerParameters cp)
698 DateTime tim = DateTime.Now;
700 XmlMapping[] maps = batch.Maps;
703 cp = new CompilerParameters();
704 cp.IncludeDebugInformation = false;
705 cp.GenerateInMemory = true;
706 cp.TempFiles.KeepFiles = !deleteTempFiles;
709 string file = cp.TempFiles.AddExtension ("cs");
710 StreamWriter sw = new StreamWriter (file);
712 if (!deleteTempFiles)
713 Console.WriteLine ("Generating " + file);
715 SerializationCodeGenerator gen = new SerializationCodeGenerator (maps);
719 gen.GenerateSerializers (sw);
723 Console.WriteLine ("Serializer could not be generated");
724 Console.WriteLine (ex);
725 cp.TempFiles.Delete ();
730 CSharpCodeProvider provider = new CSharpCodeProvider();
731 ICodeCompiler comp = provider.CreateCompiler ();
733 cp.GenerateExecutable = false;
735 foreach (Type rtype in gen.ReferencedTypes)
737 if (!cp.ReferencedAssemblies.Contains (rtype.Assembly.Location))
738 cp.ReferencedAssemblies.Add (rtype.Assembly.Location);
741 if (!cp.ReferencedAssemblies.Contains ("System.dll"))
742 cp.ReferencedAssemblies.Add ("System.dll");
743 if (!cp.ReferencedAssemblies.Contains ("System.Xml"))
744 cp.ReferencedAssemblies.Add ("System.Xml");
745 if (!cp.ReferencedAssemblies.Contains ("System.Data"))
746 cp.ReferencedAssemblies.Add ("System.Data");
748 CompilerResults res = comp.CompileAssemblyFromFile (cp, file);
749 if (res.Errors.HasErrors || res.CompiledAssembly == null) {
750 Console.WriteLine ("Error while compiling generated serializer");
751 foreach (CompilerError error in res.Errors)
752 Console.WriteLine (error);
754 cp.TempFiles.Delete ();
758 GenerationResult[] results = gen.GenerationResults;
759 for (int n=0; n<results.Length; n++)
761 GenerationResult gres = results[n];
762 SerializerData sd = batch.Datas [n];
765 sd.WriterType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.WriterClassName);
766 sd.ReaderType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.ReaderClassName);
767 sd.WriterMethod = sd.WriterType.GetMethod (gres.WriteMethodName);
768 sd.ReaderMethod = sd.ReaderType.GetMethod (gres.ReadMethodName);
773 cp.TempFiles.Delete ();
775 if (!deleteTempFiles)
776 Console.WriteLine ("Generation finished - " + (DateTime.Now - tim).TotalMilliseconds + " ms");
778 return res.CompiledAssembly;
783 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
788 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
794 #endregion // Methods