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");
153 #if !MOBILE && CONFIGURATION_DEP
154 // DiagnosticsSection
155 ConfigurationSection table
= (ConfigurationSection
) ConfigurationSettings
.GetConfig("system.diagnostics");
156 var bf
= BindingFlags
.Public
| BindingFlags
.NonPublic
| BindingFlags
.Instance
;
159 // SwitchElementsCollection
160 var pi
= table
.GetType ().GetProperty ("Switches", bf
);
161 var switchesElement
= (ConfigurationElementCollection
) pi
.GetValue (table
, null);
162 foreach (ConfigurationElement e
in switchesElement
) {
164 if (e
.GetType ().GetProperty ("Name", bf
).GetValue (e
, null) as string == "XmlSerialization.Compilation") {
165 if (e
.GetType ().GetProperty ("Value", bf
).GetValue (e
, null) as string == "1")
166 deleteTempFiles
= false;
176 protected XmlSerializer ()
178 customSerializer
= true;
181 public XmlSerializer (Type type
)
182 : this (type
, null, null, null, null)
186 public XmlSerializer (XmlTypeMapping xmlTypeMapping
)
188 typeMapping
= xmlTypeMapping
;
191 internal XmlSerializer (XmlMapping mapping
, SerializerData data
)
193 typeMapping
= mapping
;
194 serializerData
= data
;
197 public XmlSerializer (Type type
, string defaultNamespace
)
198 : this (type
, null, null, null, defaultNamespace
)
202 public XmlSerializer (Type type
, Type
[] extraTypes
)
203 : this (type
, null, extraTypes
, null, null)
207 public XmlSerializer (Type type
, XmlAttributeOverrides overrides
, Type
[] extraTypes
, XmlRootAttribute root
, string defaultNamespace
, string location
)
208 : this (type
, overrides
, extraTypes
, root
, defaultNamespace
, location
, null)
212 public XmlSerializer (Type type
, XmlAttributeOverrides overrides
)
213 : this (type
, overrides
, null, null, null)
217 public XmlSerializer (Type type
, XmlRootAttribute root
)
218 : this (type
, null, null, root
, null)
222 public XmlSerializer (Type type
,
223 XmlAttributeOverrides overrides
,
225 XmlRootAttribute root
,
226 string defaultNamespace
)
229 throw new ArgumentNullException ("type");
231 XmlReflectionImporter importer
= new XmlReflectionImporter (overrides
, defaultNamespace
);
233 if (extraTypes
!= null)
235 foreach (Type intype
in extraTypes
)
236 importer
.IncludeType (intype
);
239 typeMapping
= importer
.ImportTypeMapping (type
, root
, defaultNamespace
);
242 internal XmlMapping Mapping
244 get { return typeMapping; }
249 public XmlSerializer (Type type
,
250 XmlAttributeOverrides overrides
,
252 XmlRootAttribute root
,
253 string defaultNamespace
,
259 #endregion // Constructors
262 private UnreferencedObjectEventHandler onUnreferencedObject
;
263 private XmlAttributeEventHandler onUnknownAttribute
;
264 private XmlElementEventHandler onUnknownElement
;
265 private XmlNodeEventHandler onUnknownNode
;
267 public event XmlAttributeEventHandler UnknownAttribute
269 add { onUnknownAttribute += value; }
remove { onUnknownAttribute -= value; }
272 public event XmlElementEventHandler UnknownElement
274 add { onUnknownElement += value; }
remove { onUnknownElement -= value; }
277 public event XmlNodeEventHandler UnknownNode
279 add { onUnknownNode += value; }
remove { onUnknownNode -= value; }
283 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e
)
285 if (onUnknownAttribute
!= null) onUnknownAttribute(this, e
);
288 internal virtual void OnUnknownElement (XmlElementEventArgs e
)
290 if (onUnknownElement
!= null) onUnknownElement(this, e
);
293 internal virtual void OnUnknownNode (XmlNodeEventArgs e
)
295 if (onUnknownNode
!= null) onUnknownNode(this, e
);
298 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e
)
300 if (onUnreferencedObject
!= null) onUnreferencedObject(this, e
);
303 public event UnreferencedObjectEventHandler UnreferencedObject
305 add { onUnreferencedObject += value; }
remove { onUnreferencedObject -= value; }
312 public virtual bool CanDeserialize (XmlReader xmlReader
)
314 xmlReader
.MoveToContent ();
315 if (typeMapping
is XmlMembersMapping
)
318 return ((XmlTypeMapping
)typeMapping
).ElementName
== xmlReader
.LocalName
;
321 protected virtual XmlSerializationReader
CreateReader ()
323 // Must be implemented in derived class
324 throw new NotImplementedException ();
327 protected virtual XmlSerializationWriter
CreateWriter ()
329 // Must be implemented in derived class
330 throw new NotImplementedException ();
333 public object Deserialize (Stream stream
)
335 XmlTextReader xmlReader
= new XmlTextReader(stream
);
336 xmlReader
.Normalization
= true;
337 xmlReader
.WhitespaceHandling
= WhitespaceHandling
.Significant
;
338 return Deserialize(xmlReader
);
341 public object Deserialize (TextReader textReader
)
343 XmlTextReader xmlReader
= new XmlTextReader(textReader
);
344 xmlReader
.Normalization
= true;
345 xmlReader
.WhitespaceHandling
= WhitespaceHandling
.Significant
;
346 return Deserialize(xmlReader
);
349 public object Deserialize (XmlReader xmlReader
)
351 XmlSerializationReader xsReader
;
352 if (customSerializer
)
353 xsReader
= CreateReader ();
355 xsReader
= CreateReader (typeMapping
);
357 xsReader
.Initialize (xmlReader
, this);
358 return Deserialize (xsReader
);
361 protected virtual object Deserialize (XmlSerializationReader reader
)
363 if (customSerializer
)
364 // Must be implemented in derived class
365 throw new NotImplementedException ();
368 if (reader
is XmlSerializationReaderInterpreter
)
369 return ((XmlSerializationReaderInterpreter
) reader
).ReadRoot ();
372 return serializerData
.ReaderMethod
.Invoke (reader
, null);
373 } catch (TargetInvocationException ex
) {
374 throw ex
.InnerException
;
377 } catch (Exception ex
) {
378 if (ex
is InvalidOperationException
|| ex
is InvalidCastException
)
379 throw new InvalidOperationException ("There is an error in"
380 + " XML document.", ex
);
385 public static XmlSerializer
[] FromMappings (XmlMapping
[] mappings
)
387 XmlSerializer
[] sers
= new XmlSerializer
[mappings
.Length
];
388 SerializerData
[] datas
= new SerializerData
[mappings
.Length
];
389 GenerationBatch batch
= new GenerationBatch ();
390 batch
.Maps
= mappings
;
393 for (int n
=0; n
<mappings
.Length
; n
++)
395 if (mappings
[n
] != null)
397 SerializerData data
= new SerializerData ();
399 sers
[n
] = new XmlSerializer (mappings
[n
], data
);
407 public static XmlSerializer
[] FromTypes (Type
[] types
)
409 XmlSerializer
[] sers
= new XmlSerializer
[types
.Length
];
410 for (int n
=0; n
<types
.Length
; n
++)
411 sers
[n
] = new XmlSerializer (types
[n
]);
415 protected virtual void Serialize (object o
, XmlSerializationWriter writer
)
417 if (customSerializer
)
418 // Must be implemented in derived class
419 throw new NotImplementedException ();
421 if (writer
is XmlSerializationWriterInterpreter
)
422 ((XmlSerializationWriterInterpreter
)writer
).WriteRoot (o
);
425 serializerData
.WriterMethod
.Invoke (writer
, new object[] {o}
);
426 } catch (TargetInvocationException ex
) {
427 throw ex
.InnerException
;
432 public void Serialize (Stream stream
, object o
)
434 Serialize (stream
, o
, null);
437 public void Serialize (TextWriter textWriter
, object o
)
439 XmlTextWriter xmlWriter
= new XmlTextWriter (textWriter
);
440 xmlWriter
.Formatting
= Formatting
.Indented
;
441 Serialize (xmlWriter
, o
, null);
444 public void Serialize (XmlWriter xmlWriter
, object o
)
446 Serialize (xmlWriter
, o
, null);
449 public void Serialize (Stream stream
, object o
, XmlSerializerNamespaces namespaces
)
451 XmlTextWriter xmlWriter
= new XmlTextWriter (stream
, Encoding
.UTF8
);
452 xmlWriter
.Formatting
= Formatting
.Indented
;
453 Serialize (xmlWriter
, o
, namespaces
);
456 public void Serialize (TextWriter textWriter
, object o
, XmlSerializerNamespaces namespaces
)
458 XmlTextWriter xmlWriter
= new XmlTextWriter (textWriter
);
459 xmlWriter
.Formatting
= Formatting
.Indented
;
460 Serialize (xmlWriter
, o
, namespaces
);
464 public void Serialize (XmlWriter xmlWriter
, object o
, XmlSerializerNamespaces namespaces
)
466 XmlSerializationWriter xsWriter
;
469 if (customSerializer
)
470 xsWriter
= CreateWriter ();
472 xsWriter
= CreateWriter (typeMapping
);
474 if (namespaces
== null || namespaces
.Count
== 0) {
475 namespaces
= new XmlSerializerNamespaces ();
476 namespaces
.Add ("xsi", XmlSchema
.InstanceNamespace
);
477 namespaces
.Add ("xsd", XmlSchema
.Namespace
);
480 xsWriter
.Initialize (xmlWriter
, namespaces
);
481 Serialize (o
, xsWriter
);
483 } catch (Exception ex
) {
484 if (ex
is TargetInvocationException
)
485 ex
= ex
.InnerException
;
487 if (ex
is InvalidOperationException
|| ex
is InvalidCastException
)
488 throw new InvalidOperationException ("There was an error generating" +
489 " 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 ();
570 XmlSerializationWriter
CreateWriter (XmlMapping typeMapping
)
572 XmlSerializationWriter writer
;
575 if (serializerData
!= null) {
576 lock (serializerData
) {
577 writer
= serializerData
.CreateWriter ();
579 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");
599 return new XmlSerializationWriterInterpreter (typeMapping
);
602 XmlSerializationReader
CreateReader (XmlMapping typeMapping
)
605 XmlSerializationReader reader
;
608 if (serializerData
!= null) {
609 lock (serializerData
) {
610 reader
= serializerData
.CreateReader ();
612 if (reader
!= null) return reader
;
616 if (!typeMapping
.Source
.CanBeGenerated
|| generationThreshold
== -1)
617 return new XmlSerializationReaderInterpreter (typeMapping
);
619 CheckGeneratedTypes (typeMapping
);
622 lock (serializerData
) {
623 reader
= serializerData
.CreateReader ();
625 if (reader
!= null) return reader
;
626 if (!generatorFallback
)
627 throw new InvalidOperationException ("Error while generating serializer");
631 return new XmlSerializationReaderInterpreter (typeMapping
);
635 void CheckGeneratedTypes (XmlMapping typeMapping
)
637 throw new NotImplementedException();
639 void GenerateSerializersAsync (GenerationBatch batch
)
641 throw new NotImplementedException();
643 void RunSerializerGeneration (object obj
)
645 throw new NotImplementedException();
648 void CheckGeneratedTypes (XmlMapping typeMapping
)
652 if (serializerData
== null)
654 lock (serializerTypes
)
656 serializerData
= (SerializerData
) serializerTypes
[typeMapping
.Source
];
657 if (serializerData
== null) {
658 serializerData
= new SerializerData();
659 serializerTypes
[typeMapping
.Source
] = serializerData
;
665 bool generate
= false;
666 lock (serializerData
)
668 if (serializerData
.UsageCount
>= generationThreshold
&& !serializerData
.Generated
)
669 serializerData
.Generated
= generate
= true;
671 serializerData
.UsageCount
++;
676 if (serializerData
.Batch
!= null)
677 GenerateSerializersAsync (serializerData
.Batch
);
680 GenerationBatch batch
= new GenerationBatch ();
681 batch
.Maps
= new XmlMapping
[] {typeMapping}
;
682 batch
.Datas
= new SerializerData
[] {serializerData}
;
683 GenerateSerializersAsync (batch
);
688 void GenerateSerializersAsync (GenerationBatch batch
)
690 if (batch
.Maps
.Length
!= batch
.Datas
.Length
)
691 throw new ArgumentException ("batch");
695 if (batch
.Done
) return;
699 if (backgroundGeneration
)
700 ThreadPool
.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration
), batch
);
702 RunSerializerGeneration (batch
);
705 void RunSerializerGeneration (object obj
)
709 GenerationBatch batch
= (GenerationBatch
) obj
;
710 batch
= LoadFromSatelliteAssembly (batch
);
713 GenerateSerializers (batch
, null);
717 Console
.WriteLine (ex
);
721 static Assembly
GenerateSerializers (GenerationBatch batch
, CompilerParameters cp
)
723 DateTime tim
= DateTime
.Now
;
725 XmlMapping
[] maps
= batch
.Maps
;
728 cp
= new CompilerParameters();
729 cp
.IncludeDebugInformation
= false;
730 cp
.GenerateInMemory
= true;
731 cp
.TempFiles
.KeepFiles
= !deleteTempFiles
;
734 string file
= cp
.TempFiles
.AddExtension ("cs");
735 StreamWriter sw
= new StreamWriter (file
);
737 if (!deleteTempFiles
)
738 Console
.WriteLine ("Generating " + file
);
740 SerializationCodeGenerator gen
= new SerializationCodeGenerator (maps
);
744 gen
.GenerateSerializers (sw
);
748 Console
.WriteLine ("Serializer could not be generated");
749 Console
.WriteLine (ex
);
750 cp
.TempFiles
.Delete ();
755 CSharpCodeProvider provider
= new CSharpCodeProvider();
756 ICodeCompiler comp
= provider
.CreateCompiler ();
758 cp
.GenerateExecutable
= false;
760 foreach (Type rtype
in gen
.ReferencedTypes
)
762 string path
= new Uri (rtype
.Assembly
.CodeBase
).LocalPath
;
763 if (!cp
.ReferencedAssemblies
.Contains (path
))
764 cp
.ReferencedAssemblies
.Add (path
);
767 if (!cp
.ReferencedAssemblies
.Contains ("System.dll"))
768 cp
.ReferencedAssemblies
.Add ("System.dll");
769 if (!cp
.ReferencedAssemblies
.Contains ("System.Xml"))
770 cp
.ReferencedAssemblies
.Add ("System.Xml");
771 if (!cp
.ReferencedAssemblies
.Contains ("System.Data"))
772 cp
.ReferencedAssemblies
.Add ("System.Data");
773 if (!cp
.ReferencedAssemblies
.Contains ("System.Web.Services"))
774 cp
.ReferencedAssemblies
.Add ("System.Web.Services");
776 CompilerResults res
= comp
.CompileAssemblyFromFile (cp
, file
);
777 if (res
.Errors
.HasErrors
|| res
.CompiledAssembly
== null) {
778 Console
.WriteLine ("Error while compiling generated serializer");
779 foreach (CompilerError error
in res
.Errors
)
780 Console
.WriteLine (error
);
782 cp
.TempFiles
.Delete ();
786 GenerationResult
[] results
= gen
.GenerationResults
;
787 for (int n
=0; n
<results
.Length
; n
++)
789 GenerationResult gres
= results
[n
];
790 SerializerData sd
= batch
.Datas
[n
];
793 sd
.WriterType
= res
.CompiledAssembly
.GetType (gres
.Namespace
+ "." + gres
.WriterClassName
);
794 sd
.ReaderType
= res
.CompiledAssembly
.GetType (gres
.Namespace
+ "." + gres
.ReaderClassName
);
795 sd
.WriterMethod
= sd
.WriterType
.GetMethod (gres
.WriteMethodName
);
796 sd
.ReaderMethod
= sd
.ReaderType
.GetMethod (gres
.ReadMethodName
);
801 cp
.TempFiles
.Delete ();
803 if (!deleteTempFiles
)
804 Console
.WriteLine ("Generation finished - " + (DateTime
.Now
- tim
).TotalMilliseconds
+ " ms");
806 return res
.CompiledAssembly
;
810 GenerationBatch
LoadFromSatelliteAssembly (GenerationBatch batch
)
815 #endregion // Methods