BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / XmlFormatterSerializer.cs
1 //
2 // XmlFormatterSerializer.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 #if NET_2_0
29 using System;
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Reflection;
33 using System.Runtime.Serialization.Formatters.Binary;
34 using System.Xml;
35 using System.Xml.Schema;
36
37 using QName = System.Xml.XmlQualifiedName;
38
39 namespace System.Runtime.Serialization
40 {
41         internal class XmlFormatterSerializer
42         {
43                 XmlDictionaryWriter writer;
44                 object graph;
45                 KnownTypeCollection types;
46                 
47                 bool save_id;
48                 bool ignore_unknown;
49                 IDataContractSurrogate surrogate;
50                 DataContractResolver resolver, default_resolver; // new in 4.0
51                 int max_items;
52
53                 List<object> objects = new List<object> ();
54                 Dictionary<object,string> references = new Dictionary<object,string> (); // preserve possibly referenced objects to ids. (new in 3.5 SP1)
55
56                 public static void Serialize (XmlDictionaryWriter writer, object graph, Type declaredType, KnownTypeCollection types,
57                         bool ignoreUnknown, int maxItems, string root_ns, bool preserveObjectReferences, DataContractResolver resolver, DataContractResolver defaultResolver)
58                 {
59                         new XmlFormatterSerializer (writer, types, ignoreUnknown, maxItems, root_ns, preserveObjectReferences, resolver, defaultResolver)
60                                 .Serialize (/*graph != null ? graph.GetType () : */declaredType, graph); // FIXME: I believe it should always use declaredType, but such a change brings some test breakages.
61                 }
62
63                 public XmlFormatterSerializer (XmlDictionaryWriter writer, KnownTypeCollection types, bool ignoreUnknown,
64                                                int maxItems, string root_ns, bool preserveObjectReferences,
65                                                DataContractResolver resolver, DataContractResolver defaultResolver)
66                 {
67                         this.writer = writer;
68                         this.types = types;
69                         ignore_unknown = ignoreUnknown;
70                         max_items = maxItems;
71                         PreserveObjectReferences = preserveObjectReferences;
72                         this.resolver = resolver;
73                         this.default_resolver = defaultResolver;
74                 }
75
76                 public bool PreserveObjectReferences { get; private set; }
77
78                 public List<object> SerializingObjects {
79                         get { return objects; }
80                 }
81
82                 public XmlDictionaryWriter Writer {
83                         get { return writer; }
84                 }
85
86                 public void Serialize (Type type, object graph)
87                 {
88                         if (graph == null)
89                                 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
90 #if !MOONLIGHT
91                         else if (type == typeof (XmlElement))
92                                 ((XmlElement) graph).WriteTo (Writer);
93                         else if (type == typeof (XmlNode [])) {
94                                 foreach (var xn in (XmlNode []) graph)
95                                         xn.WriteTo (Writer);
96                         }
97 #endif
98                         else {
99                                 QName resolvedQName = null;
100                                 if (resolver != null) {
101                                         XmlDictionaryString rname, rns;
102                                         if (resolver.TryResolveType (graph != null ? graph.GetType () : typeof (object), type, default_resolver, out rname, out rns))
103                                                 resolvedQName = new QName (rname.Value, rns.Value);
104                                 }
105
106                                 Type actualType = graph.GetType ();
107
108                                 SerializationMap map;
109                                 map = types.FindUserMap (actualType);
110                                 // For some collection types, the actual type does not matter. So get nominal serialization type instead.
111                                 // (The code below also covers the lines above, but I don't remove above lines to avoid extra search cost.)
112                                 if (map == null) {
113                                         // FIXME: not sure if type.IsInterface is the correct condition to determine whether items are serialized with i:type or not. (e.g. bug #675144 server response).
114                                         actualType = types.GetSerializedType (type.IsInterface ? type : actualType);
115                                         map = types.FindUserMap (actualType);
116                                 }
117                                 // If it is still unknown, then register it.
118                                 if (map == null) {
119                                         types.Add (actualType);
120                                         map = types.FindUserMap (actualType);
121                                 }
122
123                                 if (actualType != type && (map == null || map.OutputXsiType)) {
124                                         QName qname = resolvedQName ?? types.GetXmlName (actualType);
125                                         string name = qname.Name;
126                                         string ns = qname.Namespace;
127                                         if (qname == QName.Empty) {
128                                                 name = XmlConvert.EncodeLocalName (actualType.Name);
129                                                 ns = KnownTypeCollection.DefaultClrNamespaceBase + actualType.Namespace;
130                                         } else if (qname.Namespace == KnownTypeCollection.MSSimpleNamespace)
131                                                 ns = XmlSchema.Namespace;
132                                         if (writer.LookupPrefix (ns) == null) // it goes first (extraneous, but it makes att order compatible)
133                                                 writer.WriteXmlnsAttribute (null, ns);
134                                         writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
135                                         writer.WriteQualifiedName (name, ns);
136                                         writer.WriteEndAttribute ();
137                                 }
138                                 QName predef = KnownTypeCollection.GetPredefinedTypeName (actualType);
139                                 if (predef != QName.Empty)
140                                         SerializePrimitive (type, graph, predef);
141                                 else
142                                         map.Serialize (graph, this);
143                         }
144                 }
145
146                 public void SerializePrimitive (Type type, object graph, QName qname)
147                 {
148                         string label;
149                         if (TrySerializeAsReference (false, graph, out label))
150                                 return;
151                         if (label != null)
152                                 Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
153
154 //                      writer.WriteStartAttribute ("type", XmlSchema.InstanceNamespace);
155 //                      writer.WriteQualifiedName (qname.Name, qname.Namespace);
156 //                      writer.WriteEndAttribute ();
157
158                         // It is the only exceptional type that does not serialize to string but serializes into complex element.
159                         if (type == typeof (DateTimeOffset)) {
160                                 var v = (DateTimeOffset) graph;
161                                 writer.WriteStartElement ("DateTime", KnownTypeCollection.DefaultClrNamespaceSystem);
162                                 SerializePrimitive (typeof (DateTime), DateTime.SpecifyKind (v.DateTime.Subtract (v.Offset), DateTimeKind.Utc), KnownTypeCollection.GetPredefinedTypeName (typeof (DateTime)));
163                                 writer.WriteEndElement ();
164                                 writer.WriteStartElement ("OffsetMinutes", KnownTypeCollection.DefaultClrNamespaceSystem);
165                                 SerializePrimitive (typeof (int), v.Offset.TotalMinutes, KnownTypeCollection.GetPredefinedTypeName (typeof (int)));
166                                 writer.WriteEndElement ();
167                         }
168                         else
169                                 writer.WriteString (KnownTypeCollection.PredefinedTypeObjectToString (graph));
170                 }
171
172                 public void WriteStartElement (string memberName, string memberNamespace, string contentNamespace)
173                 {
174                         writer.WriteStartElement (memberName, memberNamespace);
175                         if (!string.IsNullOrEmpty (contentNamespace) && contentNamespace != memberNamespace)
176                                 writer.WriteXmlnsAttribute (null, contentNamespace);
177                 }
178
179                 public void WriteEndElement ()
180                 {
181                         writer.WriteEndElement ();
182                 }
183
184                 // returned bool: whether z:Ref is written or not.
185                 // out label: object label either in use or newly allocated.
186                 public bool TrySerializeAsReference (bool isMapReference, object graph, out string label)
187                 {
188                         label = null;
189                         if (!isMapReference && (!PreserveObjectReferences || graph == null || graph.GetType ().IsValueType))
190                                 return false;
191
192                         if (references.TryGetValue (graph, out label)) {
193                                 Writer.WriteAttributeString ("z", "Ref", KnownTypeCollection.MSSimpleNamespace, label);
194                                 label = null; // do not write label
195                                 return true;
196                         }
197
198                         label = "i" + (references.Count + 1);
199                         references.Add (graph, label);
200
201                         return false;
202                 }
203         }
204 }
205 #endif