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
;
41 using System
.CodeDom
.Compiler
;
42 using Microsoft
.CSharp
;
43 using System
.Configuration
;
44 using System
.Security
.Policy
;
46 namespace System
.Xml
.Serialization
49 public class XmlSerializer
51 internal const string WsdlNamespace
= "http://schemas.xmlsoap.org/wsdl/";
52 internal const string EncodingNamespace
= "http://schemas.xmlsoap.org/soap/encoding/";
53 static int generationThreshold
;
54 static bool backgroundGeneration
= true;
55 static bool deleteTempFiles
= true;
57 bool customSerializer
;
58 XmlMapping typeMapping
;
60 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
;
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 ()
104 string db
= Environment
.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
105 deleteTempFiles
= (db
== null || db
== "no");
107 IDictionary table
= (IDictionary
) ConfigurationSettings
.GetConfig("system.diagnostics");
109 table
= (IDictionary
) table
["switches"];
111 string val
= (string) table
["XmlSerialization.Compilation"];
112 if (val
== "1") deleteTempFiles
= false;
116 string th
= Environment
.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
119 generationThreshold
= 50;
120 backgroundGeneration
= true;
122 else if (th
.ToLower(CultureInfo
.InvariantCulture
) == "no")
123 generationThreshold
= -1;
125 generationThreshold
= int.Parse (th
, CultureInfo
.InvariantCulture
);
126 backgroundGeneration
= (generationThreshold
!= 0);
127 if (generationThreshold
< 1) generationThreshold
= 1;
133 protected XmlSerializer ()
135 customSerializer
= true;
138 public XmlSerializer (Type type
)
139 : this (type
, null, null, null, null)
143 public XmlSerializer (XmlTypeMapping xmlTypeMapping
)
145 typeMapping
= xmlTypeMapping
;
148 internal XmlSerializer (XmlMapping mapping
, SerializerData data
)
150 typeMapping
= mapping
;
151 serializerData
= data
;
154 public XmlSerializer (Type type
, string defaultNamespace
)
155 : this (type
, null, null, null, defaultNamespace
)
159 public XmlSerializer (Type type
, Type
[] extraTypes
)
160 : this (type
, null, extraTypes
, null, null)
164 public XmlSerializer (Type type
, XmlAttributeOverrides overrides
)
165 : this (type
, overrides
, null, null, null)
169 public XmlSerializer (Type type
, XmlRootAttribute root
)
170 : this (type
, null, null, root
, null)
174 public XmlSerializer (Type type
,
175 XmlAttributeOverrides overrides
,
177 XmlRootAttribute root
,
178 string defaultNamespace
)
181 throw new ArgumentNullException ("type");
183 XmlReflectionImporter importer
= new XmlReflectionImporter (overrides
, defaultNamespace
);
185 if (extraTypes
!= null)
187 foreach (Type intype
in extraTypes
)
188 importer
.IncludeType (intype
);
191 typeMapping
= importer
.ImportTypeMapping (type
, root
, defaultNamespace
);
194 internal XmlMapping Mapping
196 get { return typeMapping; }
202 public XmlSerializer (Type type
,
203 XmlAttributeOverrides overrides
,
205 XmlRootAttribute root
,
206 string defaultNamespace
,
213 #endregion // Constructors
217 private XmlAttributeEventHandler onUnknownAttribute
;
218 private XmlElementEventHandler onUnknownElement
;
219 private XmlNodeEventHandler onUnknownNode
;
220 private UnreferencedObjectEventHandler onUnreferencedObject
;
222 public event XmlAttributeEventHandler UnknownAttribute
224 add { onUnknownAttribute += value; }
remove { onUnknownAttribute -= value; }
227 public event XmlElementEventHandler UnknownElement
229 add { onUnknownElement += value; }
remove { onUnknownElement -= value; }
232 public event XmlNodeEventHandler UnknownNode
234 add { onUnknownNode += value; }
remove { onUnknownNode -= value; }
237 public event UnreferencedObjectEventHandler UnreferencedObject
239 add { onUnreferencedObject += value; }
remove { onUnreferencedObject -= value; }
243 internal virtual void OnUnknownAttribute (XmlAttributeEventArgs e
)
245 if (onUnknownAttribute
!= null) onUnknownAttribute(this, e
);
248 internal virtual void OnUnknownElement (XmlElementEventArgs e
)
250 if (onUnknownElement
!= null) onUnknownElement(this, e
);
253 internal virtual void OnUnknownNode (XmlNodeEventArgs e
)
255 if (onUnknownNode
!= null) onUnknownNode(this, e
);
258 internal virtual void OnUnreferencedObject (UnreferencedObjectEventArgs e
)
260 if (onUnreferencedObject
!= null) onUnreferencedObject(this, e
);
268 public virtual bool CanDeserialize (XmlReader xmlReader
)
270 xmlReader
.MoveToContent ();
271 if (typeMapping
is XmlMembersMapping
)
274 return ((XmlTypeMapping
)typeMapping
).ElementName
== xmlReader
.LocalName
;
277 protected virtual XmlSerializationReader
CreateReader ()
279 // Must be implemented in derived class
280 throw new NotImplementedException ();
283 protected virtual XmlSerializationWriter
CreateWriter ()
285 // Must be implemented in derived class
286 throw new NotImplementedException ();
289 public object Deserialize (Stream stream
)
291 XmlTextReader xmlReader
= new XmlTextReader(stream
);
292 xmlReader
.Normalization
= true;
293 return Deserialize(xmlReader
);
296 public object Deserialize (TextReader textReader
)
298 XmlTextReader xmlReader
= new XmlTextReader(textReader
);
299 xmlReader
.Normalization
= true;
300 return Deserialize(xmlReader
);
303 public object Deserialize (XmlReader xmlReader
)
305 XmlSerializationReader xsReader
;
306 if (customSerializer
)
307 xsReader
= CreateReader ();
309 xsReader
= CreateReader (typeMapping
);
311 xsReader
.Initialize (xmlReader
, this);
312 return Deserialize (xsReader
);
315 protected virtual object Deserialize (XmlSerializationReader reader
)
317 if (customSerializer
)
318 // Must be implemented in derived class
319 throw new NotImplementedException ();
321 if (reader
is XmlSerializationReaderInterpreter
)
322 return ((XmlSerializationReaderInterpreter
)reader
).ReadRoot ();
324 return serializerData
.ReaderMethod
.Invoke (reader
, null);
327 public static XmlSerializer
[] FromMappings (XmlMapping
[] mappings
)
329 XmlSerializer
[] sers
= new XmlSerializer
[mappings
.Length
];
330 SerializerData
[] datas
= new SerializerData
[mappings
.Length
];
331 GenerationBatch batch
= new GenerationBatch ();
332 batch
.Maps
= mappings
;
335 for (int n
=0; n
<mappings
.Length
; n
++)
337 if (mappings
[n
] != null)
339 SerializerData data
= new SerializerData ();
341 sers
[n
] = new XmlSerializer (mappings
[n
], data
);
349 public static XmlSerializer
[] FromTypes (Type
[] mappings
)
351 XmlSerializer
[] sers
= new XmlSerializer
[mappings
.Length
];
352 for (int n
=0; n
<mappings
.Length
; n
++)
353 sers
[n
] = new XmlSerializer (mappings
[n
]);
357 protected virtual void Serialize (object o
, XmlSerializationWriter writer
)
359 if (customSerializer
)
360 // Must be implemented in derived class
361 throw new NotImplementedException ();
363 if (writer
is XmlSerializationWriterInterpreter
)
364 ((XmlSerializationWriterInterpreter
)writer
).WriteRoot (o
);
366 serializerData
.WriterMethod
.Invoke (writer
, new object[] {o}
);
369 public void Serialize (Stream stream
, object o
)
371 XmlTextWriter xmlWriter
= new XmlTextWriter (stream
, System
.Text
.Encoding
.Default
);
372 xmlWriter
.Formatting
= Formatting
.Indented
;
373 Serialize (xmlWriter
, o
, null);
376 public void Serialize (TextWriter textWriter
, object o
)
378 XmlTextWriter xmlWriter
= new XmlTextWriter (textWriter
);
379 xmlWriter
.Formatting
= Formatting
.Indented
;
380 Serialize (xmlWriter
, o
, null);
383 public void Serialize (XmlWriter xmlWriter
, object o
)
385 Serialize (xmlWriter
, o
, null);
388 public void Serialize (Stream stream
, object o
, XmlSerializerNamespaces namespaces
)
390 XmlTextWriter xmlWriter
= new XmlTextWriter (stream
, System
.Text
.Encoding
.Default
);
391 xmlWriter
.Formatting
= Formatting
.Indented
;
392 Serialize (xmlWriter
, o
, namespaces
);
395 public void Serialize (TextWriter textWriter
, object o
, XmlSerializerNamespaces namespaces
)
397 XmlTextWriter xmlWriter
= new XmlTextWriter (textWriter
);
398 xmlWriter
.Formatting
= Formatting
.Indented
;
399 Serialize (xmlWriter
, o
, namespaces
);
403 public void Serialize (XmlWriter writer
, object o
, XmlSerializerNamespaces namespaces
)
405 XmlSerializationWriter xsWriter
;
407 if (customSerializer
)
408 xsWriter
= CreateWriter ();
410 xsWriter
= CreateWriter (typeMapping
);
412 if (namespaces
== null || namespaces
.Count
== 0)
414 namespaces
= new XmlSerializerNamespaces ();
415 namespaces
.Add ("xsd", XmlSchema
.Namespace
);
416 namespaces
.Add ("xsi", XmlSchema
.InstanceNamespace
);
419 xsWriter
.Initialize (writer
, namespaces
);
420 Serialize (o
, xsWriter
);
427 public object Deserialize (XmlReader xmlReader
, string encodingStyle
, XmlDeserializationEvents events
)
429 throw new NotImplementedException ();
433 public object Deserialize (XmlReader xmlReader
, string encodingStyle
)
435 throw new NotImplementedException ();
439 public object Deserialize (XmlReader xmlReader
, XmlDeserializationEvents events
)
441 throw new NotImplementedException ();
445 public static XmlSerializer
[] FromMappings (XmlMapping
[] mappings
, Evidence evidence
)
447 throw new NotImplementedException ();
451 public static XmlSerializer
[] FromMappings (XmlMapping
[] mappings
, Type type
)
453 throw new NotImplementedException ();
456 public static Assembly
GenerateSerializer (Type
[] types
, XmlMapping
[] mappings
)
458 return GenerateSerializer (types
, mappings
, null);
462 public static Assembly
GenerateSerializer (Type
[] types
, XmlMapping
[] mappings
, CompilerParameters parameters
)
464 GenerationBatch batch
= new GenerationBatch ();
465 batch
.Maps
= mappings
;
466 batch
.Datas
= new SerializerData
[mappings
.Length
];
468 for (int n
=0; n
<mappings
.Length
; n
++) {
469 SerializerData data
= new SerializerData ();
471 batch
.Datas
[n
] = data
;
474 return GenerateSerializers (batch
, parameters
);
479 public static Assembly
GenerateSerializer (Type
[] types
,
480 XmlMapping
[] mappings
,
485 throw new NotImplementedException ();
490 public static Assembly
GenerateSerializer (Type
[] types
,
491 XmlMapping
[] mappings
,
495 string compilerOptions
)
497 throw new NotImplementedException ();
500 public static string GetXmlSerializerAssemblyName (Type type
)
502 return type
.Assembly
.GetName().Name
+ ".XmlSerializers";
505 public static string GetXmlSerializerAssemblyName (Type type
, string defaultNamespace
)
507 return GetXmlSerializerAssemblyName (type
) + "." + defaultNamespace
.GetHashCode ();
511 public void Serialize (XmlWriter xmlWriter
, object o
, XmlSerializerNamespaces namespaces
, string encodingStyle
)
513 throw new NotImplementedException ();
518 XmlSerializationWriter
CreateWriter (XmlMapping typeMapping
)
520 XmlSerializationWriter writer
;
523 if (serializerData
!= null) {
524 writer
= serializerData
.CreateWriter ();
525 if (writer
!= null) return writer
;
529 if (!typeMapping
.Source
.CanBeGenerated
|| generationThreshold
== -1)
530 return new XmlSerializationWriterInterpreter (typeMapping
);
532 CheckGeneratedTypes (typeMapping
);
535 writer
= serializerData
.CreateWriter ();
536 if (writer
!= null) return writer
;
539 return new XmlSerializationWriterInterpreter (typeMapping
);
542 XmlSerializationReader
CreateReader (XmlMapping typeMapping
)
544 XmlSerializationReader reader
;
547 if (serializerData
!= null) {
548 reader
= serializerData
.CreateReader ();
549 if (reader
!= null) return reader
;
553 if (!typeMapping
.Source
.CanBeGenerated
|| generationThreshold
== -1)
554 return new XmlSerializationReaderInterpreter (typeMapping
);
556 CheckGeneratedTypes (typeMapping
);
559 reader
= serializerData
.CreateReader ();
560 if (reader
!= null) return reader
;
563 return new XmlSerializationReaderInterpreter (typeMapping
);
566 void CheckGeneratedTypes (XmlMapping typeMapping
)
570 if (serializerData
== null)
572 lock (serializerTypes
)
574 serializerData
= (SerializerData
) serializerTypes
[typeMapping
.Source
];
575 if (serializerData
== null) {
576 serializerData
= new SerializerData();
577 serializerTypes
[typeMapping
.Source
] = serializerData
;
583 bool generate
= false;
584 lock (serializerData
)
586 generate
= (++serializerData
.UsageCount
== generationThreshold
);
591 if (serializerData
.Batch
!= null)
592 GenerateSerializersAsync (serializerData
.Batch
);
595 GenerationBatch batch
= new GenerationBatch ();
596 batch
.Maps
= new XmlMapping
[] {typeMapping}
;
597 batch
.Datas
= new SerializerData
[] {serializerData}
;
598 GenerateSerializersAsync (batch
);
603 void GenerateSerializersAsync (GenerationBatch batch
)
605 if (batch
.Maps
.Length
!= batch
.Datas
.Length
)
606 throw new ArgumentException ("batch");
610 if (batch
.Done
) return;
614 if (backgroundGeneration
)
615 ThreadPool
.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration
), batch
);
617 RunSerializerGeneration (batch
);
620 void RunSerializerGeneration (object obj
)
624 GenerationBatch batch
= (GenerationBatch
) obj
;
625 batch
= LoadFromSatelliteAssembly (batch
);
628 GenerateSerializers (batch
, null);
632 Console
.WriteLine (ex
);
636 static Assembly
GenerateSerializers (GenerationBatch batch
, CompilerParameters cp
)
638 DateTime tim
= DateTime
.Now
;
640 XmlMapping
[] maps
= batch
.Maps
;
643 cp
= new CompilerParameters();
644 cp
.IncludeDebugInformation
= false;
645 cp
.GenerateInMemory
= true;
646 cp
.TempFiles
.KeepFiles
= !deleteTempFiles
;
649 string file
= cp
.TempFiles
.AddExtension ("cs");
650 StreamWriter sw
= new StreamWriter (file
);
652 if (!deleteTempFiles
)
653 Console
.WriteLine ("Generating " + file
);
655 SerializationCodeGenerator gen
= new SerializationCodeGenerator (maps
);
659 gen
.GenerateSerializers (sw
);
663 Console
.WriteLine ("Serializer could not be generated");
664 Console
.WriteLine (ex
);
665 cp
.TempFiles
.Delete ();
670 CSharpCodeProvider provider
= new CSharpCodeProvider();
671 ICodeCompiler comp
= provider
.CreateCompiler ();
673 cp
.GenerateExecutable
= false;
675 foreach (Type rtype
in gen
.ReferencedTypes
)
677 if (!cp
.ReferencedAssemblies
.Contains (rtype
.Assembly
.Location
))
678 cp
.ReferencedAssemblies
.Add (rtype
.Assembly
.Location
);
681 if (!cp
.ReferencedAssemblies
.Contains ("System.dll"))
682 cp
.ReferencedAssemblies
.Add ("System.dll");
683 if (!cp
.ReferencedAssemblies
.Contains ("System.Xml"))
684 cp
.ReferencedAssemblies
.Add ("System.Xml");
685 if (!cp
.ReferencedAssemblies
.Contains ("System.Data"))
686 cp
.ReferencedAssemblies
.Add ("System.Data");
688 CompilerResults res
= comp
.CompileAssemblyFromFile (cp
, file
);
689 if (res
.Errors
.Count
> 0)
691 Console
.WriteLine ("Error while compiling generated serializer");
692 foreach (CompilerError error
in res
.Errors
)
693 Console
.WriteLine (error
);
695 cp
.TempFiles
.Delete ();
699 GenerationResult
[] results
= gen
.GenerationResults
;
700 for (int n
=0; n
<results
.Length
; n
++)
702 GenerationResult gres
= results
[n
];
703 SerializerData sd
= batch
.Datas
[n
];
706 sd
.WriterType
= res
.CompiledAssembly
.GetType (gres
.Namespace
+ "." + gres
.WriterClassName
);
707 sd
.ReaderType
= res
.CompiledAssembly
.GetType (gres
.Namespace
+ "." + gres
.ReaderClassName
);
708 sd
.WriterMethod
= sd
.WriterType
.GetMethod (gres
.WriteMethodName
);
709 sd
.ReaderMethod
= sd
.ReaderType
.GetMethod (gres
.ReadMethodName
);
714 cp
.TempFiles
.Delete ();
716 if (!deleteTempFiles
)
717 Console
.WriteLine ("Generation finished - " + (DateTime
.Now
- tim
).TotalMilliseconds
+ " ms");
719 return res
.CompiledAssembly
;
723 GenerationBatch
LoadFromSatelliteAssembly (GenerationBatch batch
)
728 GenerationBatch
LoadFromSatelliteAssembly (GenerationBatch batch
)
734 #endregion // Methods