2 // XmlFormatterSerializer.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections.Generic;
32 using System.Reflection;
33 using System.Runtime.Serialization.Formatters.Binary;
35 using System.Xml.Schema;
37 using QName = System.Xml.XmlQualifiedName;
39 namespace System.Runtime.Serialization
41 internal class XmlFormatterSerializer
43 XmlDictionaryWriter writer;
45 KnownTypeCollection types;
49 IDataContractSurrogate surrogate;
50 DataContractResolver resolver, default_resolver; // new in 4.0
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)
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)
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.
63 public XmlFormatterSerializer (XmlDictionaryWriter writer, KnownTypeCollection types, bool ignoreUnknown,
64 int maxItems, string root_ns, bool preserveObjectReferences,
65 DataContractResolver resolver, DataContractResolver defaultResolver)
69 ignore_unknown = ignoreUnknown;
71 PreserveObjectReferences = preserveObjectReferences;
72 this.resolver = resolver;
73 this.default_resolver = defaultResolver;
76 public bool PreserveObjectReferences { get; private set; }
78 public List<object> SerializingObjects {
79 get { return objects; }
82 public XmlDictionaryWriter Writer {
83 get { return writer; }
86 public void Serialize (Type type, object graph)
89 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
91 else if (type == typeof (XmlElement))
92 ((XmlElement) graph).WriteTo (Writer);
93 else if (type == typeof (XmlNode [])) {
94 foreach (var xn in (XmlNode []) graph)
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);
106 Type actualType = graph.GetType ();
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.)
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);
117 // If it is still unknown, then register it.
119 types.Add (actualType);
120 map = types.FindUserMap (actualType);
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 ();
138 QName predef = KnownTypeCollection.GetPredefinedTypeName (actualType);
139 if (predef != QName.Empty)
140 SerializePrimitive (type, graph, predef);
142 map.Serialize (graph, this);
146 public void SerializePrimitive (Type type, object graph, QName qname)
149 if (TrySerializeAsReference (false, graph, out label))
152 Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
154 // writer.WriteStartAttribute ("type", XmlSchema.InstanceNamespace);
155 // writer.WriteQualifiedName (qname.Name, qname.Namespace);
156 // writer.WriteEndAttribute ();
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 ();
169 writer.WriteString (KnownTypeCollection.PredefinedTypeObjectToString (graph));
172 public void WriteStartElement (string memberName, string memberNamespace, string contentNamespace)
174 writer.WriteStartElement (memberName, memberNamespace);
175 if (!string.IsNullOrEmpty (contentNamespace) && contentNamespace != memberNamespace)
176 writer.WriteXmlnsAttribute (null, contentNamespace);
179 public void WriteEndElement ()
181 writer.WriteEndElement ();
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)
189 if (!isMapReference && (!PreserveObjectReferences || graph == null || graph.GetType ().IsValueType))
192 if (references.TryGetValue (graph, out label)) {
193 Writer.WriteAttributeString ("z", "Ref", KnownTypeCollection.MSSimpleNamespace, label);
194 label = null; // do not write label
198 label = "i" + (references.Count + 1);
199 references.Add (graph, label);