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;
59 bool customSerializer;
60 XmlMapping typeMapping;
62 SerializerData serializerData;
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 = null;
76 public XmlSerializationReader CreateReader () {
77 if (ReaderType != null)
78 return (XmlSerializationReader) Activator.CreateInstance (ReaderType);
79 else if (Implementation != null)
80 return Implementation.Reader;
85 public XmlSerializationWriter CreateWriter () {
86 if (WriterType != null)
87 return (XmlSerializationWriter) Activator.CreateInstance (WriterType);
88 else if (Implementation != null)
89 return Implementation.Writer;
95 internal class GenerationBatch
98 public XmlMapping[] Maps;
99 public SerializerData[] Datas;
102 static XmlSerializer ()
108 generationThreshold = -1;
109 backgroundGeneration = false;
111 string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
112 string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
115 generationThreshold = 50;
116 backgroundGeneration = true;
118 else if (th.ToLower(CultureInfo.InvariantCulture) == "no")
119 generationThreshold = -1;
121 generationThreshold = int.Parse (th, CultureInfo.InvariantCulture);
122 backgroundGeneration = (generationThreshold != 0);
123 if (generationThreshold < 1) generationThreshold = 1;
126 deleteTempFiles = (db == null || db == "no");
128 IDictionary table = (IDictionary) ConfigurationSettings.GetConfig("system.diagnostics");
131 table = (IDictionary) table["switches"];
134 string val = (string) table ["XmlSerialization.Compilation"];
135 if (val == "1") deleteTempFiles = false;
142 protected XmlSerializer ()
144 customSerializer = true;
147 public XmlSerializer (Type type)
148 : this (type, null, null, null, null)
152 public XmlSerializer (XmlTypeMapping xmlTypeMapping)
154 typeMapping = xmlTypeMapping;
157 internal XmlSerializer (XmlMapping mapping, SerializerData data)
159 typeMapping = mapping;
160 serializerData = data;
163 public XmlSerializer (Type type, string defaultNamespace)
164 : this (type, null, null, null, defaultNamespace)
168 public XmlSerializer (Type type, Type[] extraTypes)
169 : this (type, null, extraTypes, null, null)
173 public XmlSerializer (Type type, XmlAttributeOverrides overrides)
174 : this (type, overrides, null, null, null)
178 public XmlSerializer (Type type, XmlRootAttribute root)
179 : this (type, null, null, root, null)
183 public XmlSerializer (Type type,
184 XmlAttributeOverrides overrides,
186 XmlRootAttribute root,
187 string defaultNamespace)
190 throw new ArgumentNullException ("type");
192 XmlReflectionImporter importer = new XmlReflectionImporter (overrides, defaultNamespace);
194 if (extraTypes != null)
196 foreach (Type intype in extraTypes)
197 importer.IncludeType (intype);
200 typeMapping = importer.ImportTypeMapping (type, root, defaultNamespace);
203 internal XmlMapping Mapping
205 get { return typeMapping; }
211 public XmlSerializer (Type type,
212 XmlAttributeOverrides overrides,
214 XmlRootAttribute root,
215 string defaultNamespace,
222 #endregion // Constructors
226 private XmlAttributeEventHandler onUnknownAttribute;
227 private XmlElementEventHandler onUnknownElement;
228 private XmlNodeEventHandler onUnknownNode;
229 private UnreferencedObjectEventHandler onUnreferencedObject;
231 public event XmlAttributeEventHandler UnknownAttribute
233 add { onUnknownAttribute += value; } remove { onUnknownAttribute -= value; }
236 public event XmlElementEventHandler UnknownElement
238 add { onUnknownElement += value; } remove { onUnknownElement -= value; }
241 public event XmlNodeEventHandler UnknownNode
243 add { onUnknownNode += value; } remove { onUnknownNode -= value; }
246 public event UnreferencedObjectEventHandler UnreferencedObject
248 add { onUnreferencedObject += value; } remove { onUnreferencedObject -= value; }
252 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e)
254 if (onUnknownAttribute != null) onUnknownAttribute(this, e);
257 internal virtual void OnUnknownElement (XmlElementEventArgs e)
259 if (onUnknownElement != null) onUnknownElement(this, e);
262 internal virtual void OnUnknownNode (XmlNodeEventArgs e)
264 if (onUnknownNode != null) onUnknownNode(this, e);
267 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e)
269 if (onUnreferencedObject != null) onUnreferencedObject(this, e);
277 public virtual bool CanDeserialize (XmlReader xmlReader)
279 xmlReader.MoveToContent ();
280 if (typeMapping is XmlMembersMapping)
283 return ((XmlTypeMapping)typeMapping).ElementName == xmlReader.LocalName;
286 protected virtual XmlSerializationReader CreateReader ()
288 // Must be implemented in derived class
289 throw new NotImplementedException ();
292 protected virtual XmlSerializationWriter CreateWriter ()
294 // Must be implemented in derived class
295 throw new NotImplementedException ();
298 public object Deserialize (Stream stream)
300 XmlTextReader xmlReader = new XmlTextReader(stream);
301 xmlReader.Normalization = true;
302 return Deserialize(xmlReader);
305 public object Deserialize (TextReader textReader)
307 XmlTextReader xmlReader = new XmlTextReader(textReader);
308 xmlReader.Normalization = true;
309 return Deserialize(xmlReader);
312 public object Deserialize (XmlReader xmlReader)
314 XmlSerializationReader xsReader;
315 if (customSerializer)
316 xsReader = CreateReader ();
318 xsReader = CreateReader (typeMapping);
320 xsReader.Initialize (xmlReader, this);
321 return Deserialize (xsReader);
324 protected virtual object Deserialize (XmlSerializationReader reader)
326 if (customSerializer)
327 // Must be implemented in derived class
328 throw new NotImplementedException ();
330 if (reader is XmlSerializationReaderInterpreter)
331 return ((XmlSerializationReaderInterpreter)reader).ReadRoot ();
333 return serializerData.ReaderMethod.Invoke (reader, null);
336 public static XmlSerializer [] FromMappings (XmlMapping [] mappings)
338 XmlSerializer[] sers = new XmlSerializer [mappings.Length];
339 SerializerData[] datas = new SerializerData [mappings.Length];
340 GenerationBatch batch = new GenerationBatch ();
341 batch.Maps = mappings;
344 for (int n=0; n<mappings.Length; n++)
346 if (mappings[n] != null)
348 SerializerData data = new SerializerData ();
350 sers[n] = new XmlSerializer (mappings[n], data);
358 public static XmlSerializer [] FromTypes (Type [] mappings)
360 XmlSerializer [] sers = new XmlSerializer [mappings.Length];
361 for (int n=0; n<mappings.Length; n++)
362 sers[n] = new XmlSerializer (mappings[n]);
366 protected virtual void Serialize (object o, XmlSerializationWriter writer)
368 if (customSerializer)
369 // Must be implemented in derived class
370 throw new NotImplementedException ();
372 if (writer is XmlSerializationWriterInterpreter)
373 ((XmlSerializationWriterInterpreter)writer).WriteRoot (o);
375 serializerData.WriterMethod.Invoke (writer, new object[] {o});
378 public void Serialize (Stream stream, object o)
380 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
381 xmlWriter.Formatting = Formatting.Indented;
382 Serialize (xmlWriter, o, null);
385 public void Serialize (TextWriter textWriter, object o)
387 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
388 xmlWriter.Formatting = Formatting.Indented;
389 Serialize (xmlWriter, o, null);
392 public void Serialize (XmlWriter xmlWriter, object o)
394 Serialize (xmlWriter, o, null);
397 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
399 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
400 xmlWriter.Formatting = Formatting.Indented;
401 Serialize (xmlWriter, o, namespaces);
404 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
406 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
407 xmlWriter.Formatting = Formatting.Indented;
408 Serialize (xmlWriter, o, namespaces);
412 public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
414 XmlSerializationWriter xsWriter;
416 if (customSerializer)
417 xsWriter = CreateWriter ();
419 xsWriter = CreateWriter (typeMapping);
421 if (namespaces == null || namespaces.Count == 0)
423 namespaces = new XmlSerializerNamespaces ();
424 namespaces.Add ("xsd", XmlSchema.Namespace);
425 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
428 xsWriter.Initialize (writer, namespaces);
429 Serialize (o, xsWriter);
436 public object Deserialize (XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
438 throw new NotImplementedException ();
442 public object Deserialize (XmlReader xmlReader, string encodingStyle)
444 throw new NotImplementedException ();
448 public object Deserialize (XmlReader xmlReader, XmlDeserializationEvents events)
450 throw new NotImplementedException ();
454 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Evidence evidence)
456 throw new NotImplementedException ();
460 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Type type)
462 throw new NotImplementedException ();
465 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings)
467 return GenerateSerializer (types, mappings, null);
471 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings, CompilerParameters parameters)
473 GenerationBatch batch = new GenerationBatch ();
474 batch.Maps = mappings;
475 batch.Datas = new SerializerData [mappings.Length];
477 for (int n=0; n<mappings.Length; n++) {
478 SerializerData data = new SerializerData ();
480 batch.Datas [n] = data;
483 return GenerateSerializers (batch, parameters);
488 public static Assembly GenerateSerializer (Type[] types,
489 XmlMapping[] mappings,
494 throw new NotImplementedException ();
499 public static Assembly GenerateSerializer (Type[] types,
500 XmlMapping[] mappings,
504 string compilerOptions)
506 throw new NotImplementedException ();
509 public static string GetXmlSerializerAssemblyName (Type type)
511 return type.Assembly.GetName().Name + ".XmlSerializers";
514 public static string GetXmlSerializerAssemblyName (Type type, string defaultNamespace)
516 return GetXmlSerializerAssemblyName (type) + "." + defaultNamespace.GetHashCode ();
520 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces, string encodingStyle)
522 throw new NotImplementedException ();
527 XmlSerializationWriter CreateWriter (XmlMapping typeMapping)
529 XmlSerializationWriter writer;
532 if (serializerData != null) {
533 lock (serializerData) {
534 writer = serializerData.CreateWriter ();
536 if (writer != null) return writer;
540 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
541 return new XmlSerializationWriterInterpreter (typeMapping);
543 CheckGeneratedTypes (typeMapping);
546 lock (serializerData) {
547 writer = serializerData.CreateWriter ();
549 if (writer != null) return writer;
552 return new XmlSerializationWriterInterpreter (typeMapping);
555 XmlSerializationReader CreateReader (XmlMapping typeMapping)
557 XmlSerializationReader reader;
560 if (serializerData != null) {
561 lock (serializerData) {
562 reader = serializerData.CreateReader ();
564 if (reader != null) return reader;
568 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
569 return new XmlSerializationReaderInterpreter (typeMapping);
571 CheckGeneratedTypes (typeMapping);
574 lock (serializerData) {
575 reader = serializerData.CreateReader ();
577 if (reader != null) return reader;
580 return new XmlSerializationReaderInterpreter (typeMapping);
584 void CheckGeneratedTypes (XmlMapping typeMapping)
586 throw new NotImplementedException();
588 void GenerateSerializersAsync (GenerationBatch batch)
590 throw new NotImplementedException();
592 void RunSerializerGeneration (object obj)
594 throw new NotImplementedException();
597 void CheckGeneratedTypes (XmlMapping typeMapping)
601 if (serializerData == null)
603 lock (serializerTypes)
605 serializerData = (SerializerData) serializerTypes [typeMapping.Source];
606 if (serializerData == null) {
607 serializerData = new SerializerData();
608 serializerTypes [typeMapping.Source] = serializerData;
614 bool generate = false;
615 lock (serializerData)
617 generate = (++serializerData.UsageCount == generationThreshold);
622 if (serializerData.Batch != null)
623 GenerateSerializersAsync (serializerData.Batch);
626 GenerationBatch batch = new GenerationBatch ();
627 batch.Maps = new XmlMapping[] {typeMapping};
628 batch.Datas = new SerializerData[] {serializerData};
629 GenerateSerializersAsync (batch);
634 void GenerateSerializersAsync (GenerationBatch batch)
636 if (batch.Maps.Length != batch.Datas.Length)
637 throw new ArgumentException ("batch");
641 if (batch.Done) return;
645 if (backgroundGeneration)
646 ThreadPool.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration), batch);
648 RunSerializerGeneration (batch);
651 void RunSerializerGeneration (object obj)
655 GenerationBatch batch = (GenerationBatch) obj;
656 batch = LoadFromSatelliteAssembly (batch);
659 GenerateSerializers (batch, null);
663 Console.WriteLine (ex);
667 static Assembly GenerateSerializers (GenerationBatch batch, CompilerParameters cp)
669 DateTime tim = DateTime.Now;
671 XmlMapping[] maps = batch.Maps;
674 cp = new CompilerParameters();
675 cp.IncludeDebugInformation = false;
676 cp.GenerateInMemory = true;
677 cp.TempFiles.KeepFiles = !deleteTempFiles;
680 string file = cp.TempFiles.AddExtension ("cs");
681 StreamWriter sw = new StreamWriter (file);
683 if (!deleteTempFiles)
684 Console.WriteLine ("Generating " + file);
686 SerializationCodeGenerator gen = new SerializationCodeGenerator (maps);
690 gen.GenerateSerializers (sw);
694 Console.WriteLine ("Serializer could not be generated");
695 Console.WriteLine (ex);
696 cp.TempFiles.Delete ();
701 CSharpCodeProvider provider = new CSharpCodeProvider();
702 ICodeCompiler comp = provider.CreateCompiler ();
704 cp.GenerateExecutable = false;
706 foreach (Type rtype in gen.ReferencedTypes)
708 if (!cp.ReferencedAssemblies.Contains (rtype.Assembly.Location))
709 cp.ReferencedAssemblies.Add (rtype.Assembly.Location);
712 if (!cp.ReferencedAssemblies.Contains ("System.dll"))
713 cp.ReferencedAssemblies.Add ("System.dll");
714 if (!cp.ReferencedAssemblies.Contains ("System.Xml"))
715 cp.ReferencedAssemblies.Add ("System.Xml");
716 if (!cp.ReferencedAssemblies.Contains ("System.Data"))
717 cp.ReferencedAssemblies.Add ("System.Data");
719 CompilerResults res = comp.CompileAssemblyFromFile (cp, file);
720 if (res.Errors.HasErrors || res.CompiledAssembly == null) {
721 Console.WriteLine ("Error while compiling generated serializer");
722 foreach (CompilerError error in res.Errors)
723 Console.WriteLine (error);
725 cp.TempFiles.Delete ();
729 GenerationResult[] results = gen.GenerationResults;
730 for (int n=0; n<results.Length; n++)
732 GenerationResult gres = results[n];
733 SerializerData sd = batch.Datas [n];
736 sd.WriterType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.WriterClassName);
737 sd.ReaderType = res.CompiledAssembly.GetType (gres.Namespace + "." + gres.ReaderClassName);
738 sd.WriterMethod = sd.WriterType.GetMethod (gres.WriteMethodName);
739 sd.ReaderMethod = sd.ReaderType.GetMethod (gres.ReadMethodName);
744 cp.TempFiles.Delete ();
746 if (!deleteTempFiles)
747 Console.WriteLine ("Generation finished - " + (DateTime.Now - tim).TotalMilliseconds + " ms");
749 return res.CompiledAssembly;
754 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
759 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
765 #endregion // Methods