[2019-12] [bcl] Default XmlSerializer stream serialize to UTF8 Encoding (#18908)
[mono-project.git] / mcs / class / System.XML / System.Xml.Serialization / XmlSerializer.cs
blob7ef8b2ca554c79e89f5670709c323d63f934b66d
1 //
2 // XmlSerializer.cs:
3 //
4 // Author:
5 // Lluis Sanchez Gual (lluis@ximian.com)
6 //
7 // (C) 2002, 2003 Ximian, Inc. http://www.ximian.com
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
31 using System;
32 using System.Threading;
33 using System.Collections;
34 using System.Globalization;
35 using System.IO;
36 using System.Reflection;
37 using System.Xml;
38 using System.Xml.Schema;
39 using System.Text;
40 #if !MOBILE
41 using System.CodeDom;
42 using System.CodeDom.Compiler;
43 using Microsoft.CSharp;
44 #endif
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;
84 else
85 return null;
88 public XmlSerializationWriter CreateWriter () {
89 if (WriterType != null)
90 return (XmlSerializationWriter) Activator.CreateInstance (WriterType);
91 else if (Implementation != null)
92 return Implementation.Writer;
93 else
94 return null;
98 internal class GenerationBatch
100 public bool Done;
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
110 // be deleted.
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
124 #if MOBILE
125 string db = null;
126 string th = null;
127 generationThreshold = -1;
128 backgroundGeneration = false;
129 #else
130 string db = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_DEBUG");
131 string th = Environment.GetEnvironmentVariable ("MONO_XMLSERIALIZER_THS");
133 if (th == null) {
134 generationThreshold = 50;
135 backgroundGeneration = true;
136 } else {
137 int i = th.IndexOf (',');
138 if (i != -1) {
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;
146 else {
147 generationThreshold = int.Parse (th, CultureInfo.InvariantCulture);
148 backgroundGeneration = (generationThreshold != 0);
151 #endif
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;
157 if (table != null)
159 // SwitchElementsCollection
160 var pi = table.GetType ().GetProperty ("Switches", bf);
161 var switchesElement = (ConfigurationElementCollection) pi.GetValue (table, null);
162 foreach (ConfigurationElement e in switchesElement) {
163 // SwitchElement
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;
167 break;
171 #endif
174 #region Constructors
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,
224 Type [] extraTypes,
225 XmlRootAttribute root,
226 string defaultNamespace)
228 if (type == null)
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; }
248 [MonoTODO]
249 public XmlSerializer (Type type,
250 XmlAttributeOverrides overrides,
251 Type [] extraTypes,
252 XmlRootAttribute root,
253 string defaultNamespace,
254 string location,
255 Evidence evidence)
259 #endregion // Constructors
261 #region Events
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; }
308 #endregion // Events
310 #region Methods
312 public virtual bool CanDeserialize (XmlReader xmlReader)
314 xmlReader.MoveToContent ();
315 if (typeMapping is XmlMembersMapping)
316 return true;
317 else
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 ();
354 else
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 ();
367 try {
368 if (reader is XmlSerializationReaderInterpreter)
369 return ((XmlSerializationReaderInterpreter) reader).ReadRoot ();
370 else {
371 try {
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);
381 throw;
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;
391 batch.Datas = datas;
393 for (int n=0; n<mappings.Length; n++)
395 if (mappings[n] != null)
397 SerializerData data = new SerializerData ();
398 data.Batch = batch;
399 sers[n] = new XmlSerializer (mappings[n], data);
400 datas[n] = data;
404 return sers;
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]);
412 return sers;
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);
423 else {
424 try {
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);
461 xmlWriter.Flush();
464 public void Serialize (XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces)
466 XmlSerializationWriter xsWriter;
468 try {
469 if (customSerializer)
470 xsWriter = CreateWriter ();
471 else
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);
482 xmlWriter.Flush ();
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);
491 throw;
495 [MonoTODO]
496 public object Deserialize (XmlReader xmlReader, string encodingStyle, XmlDeserializationEvents events)
498 throw new NotImplementedException ();
501 [MonoTODO]
502 public object Deserialize (XmlReader xmlReader, string encodingStyle)
504 throw new NotImplementedException ();
507 [MonoTODO]
508 public object Deserialize (XmlReader xmlReader, XmlDeserializationEvents events)
510 throw new NotImplementedException ();
513 [MonoTODO]
514 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Evidence evidence)
516 throw new NotImplementedException ();
519 [MonoTODO]
520 public static XmlSerializer[] FromMappings (XmlMapping[] mappings, Type type)
522 throw new NotImplementedException ();
525 #if !MOBILE
526 public static Assembly GenerateSerializer (Type[] types, XmlMapping[] mappings)
528 return GenerateSerializer (types, mappings, null);
531 [MonoTODO]
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 ();
540 data.Batch = batch;
541 batch.Datas [n] = data;
544 return GenerateSerializers (batch, parameters);
546 #endif
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 ();
558 [MonoTODO]
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;
574 lock (this) {
575 if (serializerData != null) {
576 lock (serializerData) {
577 writer = serializerData.CreateWriter ();
579 if (writer != null) return writer;
583 #if !MOBILE
584 if (!typeMapping.Source.CanBeGenerated || generationThreshold == -1)
585 return new XmlSerializationWriterInterpreter (typeMapping);
587 CheckGeneratedTypes (typeMapping);
589 lock (this) {
590 lock (serializerData) {
591 writer = serializerData.CreateWriter ();
593 if (writer != null) return writer;
594 if (!generatorFallback)
595 throw new InvalidOperationException ("Error while generating serializer");
598 #endif
599 return new XmlSerializationWriterInterpreter (typeMapping);
602 XmlSerializationReader CreateReader (XmlMapping typeMapping)
604 #if !MOBILE
605 XmlSerializationReader reader;
607 lock (this) {
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);
621 lock (this) {
622 lock (serializerData) {
623 reader = serializerData.CreateReader ();
625 if (reader != null) return reader;
626 if (!generatorFallback)
627 throw new InvalidOperationException ("Error while generating serializer");
630 #endif
631 return new XmlSerializationReaderInterpreter (typeMapping);
634 #if MOBILE
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();
647 #else
648 void CheckGeneratedTypes (XmlMapping typeMapping)
650 lock (this)
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++;
674 if (generate)
676 if (serializerData.Batch != null)
677 GenerateSerializersAsync (serializerData.Batch);
678 else
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");
693 lock (batch)
695 if (batch.Done) return;
696 batch.Done = true;
699 if (backgroundGeneration)
700 ThreadPool.QueueUserWorkItem (new WaitCallback (RunSerializerGeneration), batch);
701 else
702 RunSerializerGeneration (batch);
705 void RunSerializerGeneration (object obj)
709 GenerationBatch batch = (GenerationBatch) obj;
710 batch = LoadFromSatelliteAssembly (batch);
712 if (batch != null)
713 GenerateSerializers (batch, null);
715 catch (Exception ex)
717 Console.WriteLine (ex);
721 static Assembly GenerateSerializers (GenerationBatch batch, CompilerParameters cp)
723 DateTime tim = DateTime.Now;
725 XmlMapping[] maps = batch.Maps;
727 if (cp == null) {
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);
746 catch (Exception ex)
748 Console.WriteLine ("Serializer could not be generated");
749 Console.WriteLine (ex);
750 cp.TempFiles.Delete ();
751 return null;
753 sw.Close ();
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 ();
783 return null;
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];
791 lock (sd)
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);
797 sd.Batch = null;
801 cp.TempFiles.Delete ();
803 if (!deleteTempFiles)
804 Console.WriteLine ("Generation finished - " + (DateTime.Now - tim).TotalMilliseconds + " ms");
806 return res.CompiledAssembly;
808 #endif
810 GenerationBatch LoadFromSatelliteAssembly (GenerationBatch batch)
812 return batch;
815 #endregion // Methods