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
;
40 #if !TARGET_JVM && !MONOTOUCH
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
123 #if TARGET_JVM || MONOTOUCH
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;
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
253 private XmlAttributeEventHandler onUnknownAttribute
;
254 private XmlElementEventHandler onUnknownElement
;
255 private XmlNodeEventHandler onUnknownNode
;
256 private UnreferencedObjectEventHandler onUnreferencedObject
;
258 public event XmlAttributeEventHandler UnknownAttribute
260 add { onUnknownAttribute += value; }
remove { onUnknownAttribute -= value; }
263 public event XmlElementEventHandler UnknownElement
265 add { onUnknownElement += value; }
remove { onUnknownElement -= value; }
268 public event XmlNodeEventHandler UnknownNode
270 add { onUnknownNode += value; }
remove { onUnknownNode -= value; }
273 public event UnreferencedObjectEventHandler UnreferencedObject
275 add { onUnreferencedObject += value; }
remove { onUnreferencedObject -= value; }
279 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e
)
281 if (onUnknownAttribute
!= null) onUnknownAttribute(this, e
);
284 internal virtual void OnUnknownElement (XmlElementEventArgs e
)
286 if (onUnknownElement
!= null) onUnknownElement(this, e
);
289 internal virtual void OnUnknownNode (XmlNodeEventArgs e
)
291 if (onUnknownNode
!= null) onUnknownNode(this, e
);
294 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e
)
296 if (onUnreferencedObject
!= null) onUnreferencedObject(this, e
);
304 public virtual bool CanDeserialize (XmlReader xmlReader
)
306 xmlReader
.MoveToContent ();
307 if (typeMapping
is XmlMembersMapping
)
310 return ((XmlTypeMapping
)typeMapping
).ElementName
== xmlReader
.LocalName
;
313 protected virtual XmlSerializationReader
CreateReader ()
315 // Must be implemented in derived class
316 throw new NotImplementedException ();
319 protected virtual XmlSerializationWriter
CreateWriter ()
321 // Must be implemented in derived class
322 throw new NotImplementedException ();
325 public object Deserialize (Stream stream
)
327 XmlTextReader xmlReader
= new XmlTextReader(stream
);
328 xmlReader
.Normalization
= true;
329 xmlReader
.WhitespaceHandling
= WhitespaceHandling
.Significant
;
330 return Deserialize(xmlReader
);
333 public object Deserialize (TextReader textReader
)
335 XmlTextReader xmlReader
= new XmlTextReader(textReader
);
336 xmlReader
.Normalization
= true;
337 xmlReader
.WhitespaceHandling
= WhitespaceHandling
.Significant
;
338 return Deserialize(xmlReader
);
341 public object Deserialize (XmlReader xmlReader
)
343 XmlSerializationReader xsReader
;
344 if (customSerializer
)
345 xsReader
= CreateReader ();
347 xsReader
= CreateReader (typeMapping
);
349 xsReader
.Initialize (xmlReader
, this);
350 return Deserialize (xsReader
);
353 protected virtual object Deserialize (XmlSerializationReader reader
)
355 if (customSerializer
)
356 // Must be implemented in derived class
357 throw new NotImplementedException ();
360 if (reader
is XmlSerializationReaderInterpreter
)
361 return ((XmlSerializationReaderInterpreter
) reader
).ReadRoot ();
363 return serializerData
.ReaderMethod
.Invoke (reader
, null);
364 } catch (Exception ex
) {
365 if (ex
is InvalidOperationException
|| ex
is InvalidCastException
)
366 throw new InvalidOperationException ("There is an error in"
367 + " XML document.", ex
);
372 public static XmlSerializer
[] FromMappings (XmlMapping
[] mappings
)
374 XmlSerializer
[] sers
= new XmlSerializer
[mappings
.Length
];
375 SerializerData
[] datas
= new SerializerData
[mappings
.Length
];
376 GenerationBatch batch
= new GenerationBatch ();
377 batch
.Maps
= mappings
;
380 for (int n
=0; n
<mappings
.Length
; n
++)
382 if (mappings
[n
] != null)
384 SerializerData data
= new SerializerData ();
386 sers
[n
] = new XmlSerializer (mappings
[n
], data
);
394 public static XmlSerializer
[] FromTypes (Type
[] mappings
)
396 XmlSerializer
[] sers
= new XmlSerializer
[mappings
.Length
];
397 for (int n
=0; n
<mappings
.Length
; n
++)
398 sers
[n
] = new XmlSerializer (mappings
[n
]);
402 protected virtual void Serialize (object o
, XmlSerializationWriter writer
)
404 if (customSerializer
)
405 // Must be implemented in derived class
406 throw new NotImplementedException ();
408 if (writer
is XmlSerializationWriterInterpreter
)
409 ((XmlSerializationWriterInterpreter
)writer
).WriteRoot (o
);
411 serializerData
.WriterMethod
.Invoke (writer
, new object[] {o}
);
414 public void Serialize (Stream stream
, object o
)
416 XmlTextWriter xmlWriter
= new XmlTextWriter (stream
, System
.Text
.Encoding
.Default
);
417 xmlWriter
.Formatting
= Formatting
.Indented
;
418 Serialize (xmlWriter
, o
, null);
421 public void Serialize (TextWriter textWriter
, object o
)
423 XmlTextWriter xmlWriter
= new XmlTextWriter (textWriter
);
424 xmlWriter
.Formatting
= Formatting
.Indented
;
425 Serialize (xmlWriter
, o
, null);
428 public void Serialize (XmlWriter xmlWriter
, object o
)
430 Serialize (xmlWriter
, o
, null);
433 public void Serialize (Stream stream
, object o
, XmlSerializerNamespaces namespaces
)
435 XmlTextWriter xmlWriter
= new XmlTextWriter (stream
, System
.Text
.Encoding
.Default
);
436 xmlWriter
.Formatting
= Formatting
.Indented
;
437 Serialize (xmlWriter
, o
, namespaces
);
440 public void Serialize (TextWriter textWriter
, object o
, XmlSerializerNamespaces namespaces
)
442 XmlTextWriter xmlWriter
= new XmlTextWriter (textWriter
);
443 xmlWriter
.Formatting
= Formatting
.Indented
;
444 Serialize (xmlWriter
, o
, namespaces
);
448 public void Serialize (XmlWriter writer
, object o
, XmlSerializerNamespaces namespaces
)
450 XmlSerializationWriter xsWriter
;
453 if (customSerializer
)
454 xsWriter
= CreateWriter ();
456 xsWriter
= CreateWriter (typeMapping
);
458 if (namespaces
== null || namespaces
.Count
== 0) {
459 namespaces
= new XmlSerializerNamespaces ();
461 namespaces
.Add ("xsi", XmlSchema
.InstanceNamespace
);
462 namespaces
.Add ("xsd", XmlSchema
.Namespace
);
464 namespaces
.Add ("xsd", XmlSchema
.Namespace
);
465 namespaces
.Add ("xsi", XmlSchema
.InstanceNamespace
);
469 xsWriter
.Initialize (writer
, namespaces
);
470 Serialize (o
, xsWriter
);
472 } catch (Exception ex
) {
473 if (ex
is TargetInvocationException
)
474 ex
= ex
.InnerException
;
476 if (ex
is InvalidOperationException
|| ex
is InvalidCastException
)
477 throw new InvalidOperationException ("There was an error generating" +
478 " the XML document.", ex
);
487 public object Deserialize (XmlReader xmlReader
, string encodingStyle
, XmlDeserializationEvents events
)
489 throw new NotImplementedException ();
493 public object Deserialize (XmlReader xmlReader
, string encodingStyle
)
495 throw new NotImplementedException ();
499 public object Deserialize (XmlReader xmlReader
, XmlDeserializationEvents events
)
501 throw new NotImplementedException ();
505 public static XmlSerializer
[] FromMappings (XmlMapping
[] mappings
, Evidence evidence
)
507 throw new NotImplementedException ();
511 public static XmlSerializer
[] FromMappings (XmlMapping
[] mappings
, Type type
)
513 throw new NotImplementedException ();
516 #if !TARGET_JVM && !MONOTOUCH
517 public static Assembly
GenerateSerializer (Type
[] types
, XmlMapping
[] mappings
)
519 return GenerateSerializer (types
, mappings
, null);
523 public static Assembly
GenerateSerializer (Type
[] types
, XmlMapping
[] mappings
, CompilerParameters parameters
)
525 GenerationBatch batch
= new GenerationBatch ();
526 batch
.Maps
= mappings
;
527 batch
.Datas
= new SerializerData
[mappings
.Length
];
529 for (int n
=0; n
<mappings
.Length
; n
++) {
530 SerializerData data
= new SerializerData ();
532 batch
.Datas
[n
] = data
;
535 return GenerateSerializers (batch
, parameters
);
539 public static string GetXmlSerializerAssemblyName (Type type
)
541 return type
.Assembly
.GetName().Name
+ ".XmlSerializers";
544 public static string GetXmlSerializerAssemblyName (Type type
, string defaultNamespace
)
546 return GetXmlSerializerAssemblyName (type
) + "." + defaultNamespace
.GetHashCode ();
550 public void Serialize (XmlWriter xmlWriter
, object o
, XmlSerializerNamespaces namespaces
, string encodingStyle
)
552 throw new NotImplementedException ();
555 [MonoNotSupported("")]
556 public void Serialize (XmlWriter xmlWriter
, Object o
, XmlSerializerNamespaces namespaces
, string encodingStyle
, string id
)
558 throw new NotImplementedException ();
562 XmlSerializationWriter
CreateWriter (XmlMapping typeMapping
)
564 XmlSerializationWriter writer
;
567 if (serializerData
!= null) {
568 lock (serializerData
) {
569 writer
= serializerData
.CreateWriter ();
571 if (writer
!= null) return writer
;
575 if (!typeMapping
.Source
.CanBeGenerated
|| generationThreshold
== -1)
576 return new XmlSerializationWriterInterpreter (typeMapping
);
578 CheckGeneratedTypes (typeMapping
);
581 lock (serializerData
) {
582 writer
= serializerData
.CreateWriter ();
584 if (writer
!= null) return writer
;
585 if (!generatorFallback
)
586 throw new InvalidOperationException ("Error while generating serializer");
589 return new XmlSerializationWriterInterpreter (typeMapping
);
592 XmlSerializationReader
CreateReader (XmlMapping typeMapping
)
594 XmlSerializationReader reader
;
597 if (serializerData
!= null) {
598 lock (serializerData
) {
599 reader
= serializerData
.CreateReader ();
601 if (reader
!= null) return reader
;
605 if (!typeMapping
.Source
.CanBeGenerated
|| generationThreshold
== -1)
606 return new XmlSerializationReaderInterpreter (typeMapping
);
608 CheckGeneratedTypes (typeMapping
);
611 lock (serializerData
) {
612 reader
= serializerData
.CreateReader ();
614 if (reader
!= null) return reader
;
615 if (!generatorFallback
)
616 throw new InvalidOperationException ("Error while generating serializer");
619 return new XmlSerializationReaderInterpreter (typeMapping
);
622 #if TARGET_JVM || MONOTOUCH
623 void CheckGeneratedTypes (XmlMapping typeMapping
)
625 throw new NotImplementedException();
627 void GenerateSerializersAsync (GenerationBatch batch
)
629 throw new NotImplementedException();
631 void RunSerializerGeneration (object obj
)
633 throw new NotImplementedException();
636 void CheckGeneratedTypes (XmlMapping typeMapping
)
640 if (serializerData
== null)
642 lock (serializerTypes
)
644 serializerData
= (SerializerData
) serializerTypes
[typeMapping
.Source
];
645 if (serializerData
== null) {
646 serializerData
= new SerializerData();
647 serializerTypes
[typeMapping
.Source
] = serializerData
;
653 bool generate
= false;
654 lock (serializerData
)
656 generate
= (++serializerData
.UsageCount
== generationThreshold
);
661 if (serializerData
.Batch
!= null)
662 GenerateSerializersAsync (serializerData
.Batch
);
665 GenerationBatch batch
= new GenerationBatch ();
666 batch
.Maps
= new XmlMapping
[] {typeMapping}
;
667 batch
.Datas
= new SerializerData
[] {serializerData}
;
668 GenerateSerializersAsync (batch
);
673 void GenerateSerializersAsync (GenerationBatch batch
)
675 if (batch
.Maps
.Length
!= batch
.Datas
.Length
)
676 throw new ArgumentException ("batch");
680 if (batch
.Done
) return;
684 if (backgroundGeneration
)
685 ThreadPool
.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration
), batch
);
687 RunSerializerGeneration (batch
);
690 void RunSerializerGeneration (object obj
)
694 GenerationBatch batch
= (GenerationBatch
) obj
;
695 batch
= LoadFromSatelliteAssembly (batch
);
698 GenerateSerializers (batch
, null);
702 Console
.WriteLine (ex
);
706 static Assembly
GenerateSerializers (GenerationBatch batch
, CompilerParameters cp
)
708 DateTime tim
= DateTime
.Now
;
710 XmlMapping
[] maps
= batch
.Maps
;
713 cp
= new CompilerParameters();
714 cp
.IncludeDebugInformation
= false;
715 cp
.GenerateInMemory
= true;
716 cp
.TempFiles
.KeepFiles
= !deleteTempFiles
;
719 string file
= cp
.TempFiles
.AddExtension ("cs");
720 StreamWriter sw
= new StreamWriter (file
);
722 if (!deleteTempFiles
)
723 Console
.WriteLine ("Generating " + file
);
725 SerializationCodeGenerator gen
= new SerializationCodeGenerator (maps
);
729 gen
.GenerateSerializers (sw
);
733 Console
.WriteLine ("Serializer could not be generated");
734 Console
.WriteLine (ex
);
735 cp
.TempFiles
.Delete ();
740 CSharpCodeProvider provider
= new CSharpCodeProvider();
741 ICodeCompiler comp
= provider
.CreateCompiler ();
743 cp
.GenerateExecutable
= false;
745 foreach (Type rtype
in gen
.ReferencedTypes
)
747 string path
= new Uri (rtype
.Assembly
.CodeBase
).LocalPath
;
748 if (!cp
.ReferencedAssemblies
.Contains (path
))
749 cp
.ReferencedAssemblies
.Add (path
);
752 if (!cp
.ReferencedAssemblies
.Contains ("System.dll"))
753 cp
.ReferencedAssemblies
.Add ("System.dll");
754 if (!cp
.ReferencedAssemblies
.Contains ("System.Xml"))
755 cp
.ReferencedAssemblies
.Add ("System.Xml");
756 if (!cp
.ReferencedAssemblies
.Contains ("System.Data"))
757 cp
.ReferencedAssemblies
.Add ("System.Data");
759 CompilerResults res
= comp
.CompileAssemblyFromFile (cp
, file
);
760 if (res
.Errors
.HasErrors
|| res
.CompiledAssembly
== null) {
761 Console
.WriteLine ("Error while compiling generated serializer");
762 foreach (CompilerError error
in res
.Errors
)
763 Console
.WriteLine (error
);
765 cp
.TempFiles
.Delete ();
769 GenerationResult
[] results
= gen
.GenerationResults
;
770 for (int n
=0; n
<results
.Length
; n
++)
772 GenerationResult gres
= results
[n
];
773 SerializerData sd
= batch
.Datas
[n
];
776 sd
.WriterType
= res
.CompiledAssembly
.GetType (gres
.Namespace
+ "." + gres
.WriterClassName
);
777 sd
.ReaderType
= res
.CompiledAssembly
.GetType (gres
.Namespace
+ "." + gres
.ReaderClassName
);
778 sd
.WriterMethod
= sd
.WriterType
.GetMethod (gres
.WriteMethodName
);
779 sd
.ReaderMethod
= sd
.ReaderType
.GetMethod (gres
.ReadMethodName
);
784 cp
.TempFiles
.Delete ();
786 if (!deleteTempFiles
)
787 Console
.WriteLine ("Generation finished - " + (DateTime
.Now
- tim
).TotalMilliseconds
+ " ms");
789 return res
.CompiledAssembly
;
794 GenerationBatch
LoadFromSatelliteAssembly (GenerationBatch batch
)
799 GenerationBatch
LoadFromSatelliteAssembly (GenerationBatch batch
)
805 #endregion // Methods