2 // JsonSerializationReader.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2008 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.
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Collections.ObjectModel;
32 using System.Globalization;
34 using System.Reflection;
38 namespace System.Runtime.Serialization.Json
40 class JsonSerializationReader
42 DataContractJsonSerializer serializer;
44 int serialized_object_count;
45 bool verify_object_name;
46 Dictionary<Type, TypeMap> typemaps = new Dictionary<Type, TypeMap> ();
49 public JsonSerializationReader (DataContractJsonSerializer serializer, XmlReader reader, Type rootType, bool verifyObjectName)
51 this.serializer = serializer;
53 this.root_type = rootType;
54 this.verify_object_name = verifyObjectName;
57 public XmlReader Reader {
58 get { return reader; }
61 public object ReadRoot ()
63 TypeMap rootMap = GetTypeMap (root_type);
65 object v = ReadObject (root_type);
69 public object ReadObject (Type type)
71 if (serialized_object_count ++ == serializer.MaxItemsInObjectGraph)
72 throw SerializationError (String.Format ("The object graph exceeded the maximum object count '{0}' specified in the serializer", serializer.MaxItemsInObjectGraph));
74 switch (Type.GetTypeCode (type)) {
76 string dbn = reader.ReadElementContentAsString ();
77 if (dbn != String.Empty)
78 throw new SerializationException (String.Format ("The only expected DBNull value string is '{{}}'. Tha actual input was '{0}'.", dbn));
81 return reader.ReadElementContentAsString ();
83 return reader.ReadElementContentAsFloat ();
85 return reader.ReadElementContentAsDouble ();
86 case TypeCode.Decimal:
87 return reader.ReadElementContentAsDecimal ();
94 int i = reader.ReadElementContentAsInt ();
96 return Enum.ToObject (type, (object)i);
98 return Convert.ChangeType (i, type, null);
100 case TypeCode.UInt64:
101 long l = reader.ReadElementContentAsLong ();
103 return Enum.ToObject (type, (object)l);
105 return Convert.ChangeType (l, type, null);
106 case TypeCode.Boolean:
107 return reader.ReadElementContentAsBoolean ();
109 if (type == typeof (Guid)) {
110 return new Guid (reader.ReadElementContentAsString ());
111 } else if (type == typeof (Uri)) {
112 return new Uri (reader.ReadElementContentAsString ());
113 } else if (type == typeof (XmlQualifiedName)) {
114 string s = reader.ReadElementContentAsString ();
115 int idx = s.IndexOf (':');
116 return idx < 0 ? new XmlQualifiedName (s) : new XmlQualifiedName (s.Substring (0, idx), s.Substring (idx + 1));
117 } else if (type != typeof (object)) {
118 // strongly-typed object
119 if (reader.IsEmptyElement) {
120 // empty -> null array or object
125 Type ct = GetCollectionType (type);
127 return DeserializeGenericCollection (type, ct);
129 TypeMap map = GetTypeMap (type);
130 return map.Deserialize (this);
134 return ReadInstanceDrivenObject ();
138 Type GetRuntimeType (string name)
140 name = ToRuntimeTypeName (name);
141 if (serializer.KnownTypes != null)
142 foreach (Type t in serializer.KnownTypes)
143 if (t.FullName == name)
145 var ret = root_type.Assembly.GetType (name, false) ?? Type.GetType (name, false);
148 #if !NET_2_1 // how to do that in ML?
149 // We probably have to iterate all the existing
150 // assemblies that are loaded in current domain.
151 foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
152 ret = ass.GetType (name, false);
160 object ReadInstanceDrivenObject ()
162 string type = reader.GetAttribute ("type");
163 if (type == "object") {
164 string runtimeType = reader.GetAttribute ("__type");
165 if (runtimeType != null) {
166 Type t = GetRuntimeType (runtimeType);
168 throw SerializationError (String.Format ("Cannot load type '{0}'", runtimeType));
169 return ReadObject (t);
172 string v = reader.ReadElementContentAsString ();
181 throw SerializationError (String.Format ("Invalid JSON boolean value: {0}", v));
187 throw SerializationError (String.Format ("Invalid JSON null value: {0}", v));
191 if (int.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out i))
194 if (long.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out l))
197 if (ulong.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out ul))
200 if (double.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dbl))
203 if (decimal.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dec))
205 throw SerializationError (String.Format ("Invalid JSON input: {0}", v));
207 throw SerializationError (String.Format ("Unexpected type: {0}", type));
211 string FormatTypeName (Type type)
213 return type.Namespace == null ? type.Name : String.Format ("{0}:#{1}", type.Name, type.Namespace);
216 string ToRuntimeTypeName (string s)
218 int idx = s.IndexOf (":#", StringComparison.Ordinal);
219 return idx < 0 ? s : String.Concat (s.Substring (idx + 2), ".", s.Substring (0, idx));
222 Type GetCollectionType (Type type)
225 return type.GetElementType ();
226 if (type.IsGenericType) {
227 // returns T for ICollection<T>
228 Type [] ifaces = type.GetInterfaces ();
229 foreach (Type i in ifaces)
230 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (ICollection<>)))
231 return i.GetGenericArguments () [0];
233 if (typeof (IList).IsAssignableFrom (type))
234 // return typeof(object) for mere collection.
235 return typeof (object);
240 object DeserializeGenericCollection (Type collectionType, Type elementType)
242 reader.ReadStartElement ();
244 if (typeof (IList).IsAssignableFrom (collectionType)) {
246 Type listType = collectionType.IsArray ? typeof (List<>).MakeGenericType (elementType) : null;
248 Type listType = collectionType.IsArray ? typeof (ArrayList) : null;
250 IList c = (IList) Activator.CreateInstance (listType ?? collectionType);
251 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
252 if (!reader.IsStartElement ("item"))
253 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
254 Type et = elementType == typeof (object) || elementType.IsAbstract ? null : elementType;
255 object elem = ReadObject (et ?? typeof (object));
259 if (collectionType.IsArray) {
260 Array array = Array.CreateInstance (elementType, c.Count);
267 ret = collectionType.IsArray ? ((ArrayList) c).ToArray (elementType) : c;
270 object c = Activator.CreateInstance (collectionType);
271 MethodInfo add = collectionType.GetMethod ("Add", new Type [] {elementType});
273 var icoll = typeof (ICollection<>).MakeGenericType (elementType);
274 if (icoll.IsAssignableFrom (c.GetType ()))
275 add = icoll.GetMethod ("Add");
278 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
279 if (!reader.IsStartElement ("item"))
280 throw SerializationError (String.Format ("Expected element 'item', but found '{0}' in namespace '{1}'", reader.LocalName, reader.NamespaceURI));
281 object elem = ReadObject (elementType);
282 add.Invoke (c, new object [] {elem});
287 reader.ReadEndElement ();
291 TypeMap GetTypeMap (Type type)
294 if (!typemaps.TryGetValue (type, out map)) {
295 map = TypeMap.CreateTypeMap (type);
296 typemaps [type] = map;
301 Exception SerializationError (string basemsg)
303 IXmlLineInfo li = reader as IXmlLineInfo;
304 if (li == null || !li.HasLineInfo ())
305 return new SerializationException (basemsg);
307 return new SerializationException (String.Format ("{0}. Error at {1} ({2},{3})", basemsg, reader.BaseURI, li.LineNumber, li.LinePosition));