2 // JsonSerializationWriter.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 JsonSerializationWriter
42 DataContractJsonSerializer serializer;
44 int serialized_object_count;
45 bool always_emit_type;
46 Dictionary<Type, TypeMap> typemaps = new Dictionary<Type, TypeMap> ();
49 public JsonSerializationWriter (DataContractJsonSerializer serializer, XmlWriter writer, Type rootType, bool alwaysEmitTypeInformation)
51 this.serializer = serializer;
53 this.root_type = rootType;
54 this.always_emit_type = alwaysEmitTypeInformation;
57 public XmlWriter Writer {
58 get { return writer; }
61 public void WriteObjectContent (object graph, bool top, bool outputTypeName)
65 GetTypeMap (root_type); // to make sure to reject invalid contracts
66 writer.WriteAttributeString ("type", "null");
67 writer.WriteString (null);
71 if (serialized_object_count ++ == serializer.MaxItemsInObjectGraph)
72 throw new SerializationException (String.Format ("The object graph exceeded the maximum object count '{0}' specified in the serializer", serializer.MaxItemsInObjectGraph));
74 var type = graph.GetType ();
75 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
76 type = type.GetGenericArguments () [0];
78 switch (Type.GetTypeCode (type)) {
81 writer.WriteString (graph.ToString ());
85 writer.WriteAttributeString ("type", "number");
86 writer.WriteString (((IFormattable) graph).ToString ("R", CultureInfo.InvariantCulture));
96 case TypeCode.Decimal:
97 writer.WriteAttributeString ("type", "number");
99 graph = ((IConvertible) graph).ToType (Enum.GetUnderlyingType (type), CultureInfo.InvariantCulture);
100 writer.WriteString (((IFormattable) graph).ToString ("G", CultureInfo.InvariantCulture));
102 case TypeCode.Boolean:
103 writer.WriteAttributeString ("type", "boolean");
105 writer.WriteString ("true");
107 writer.WriteString ("false");
109 case TypeCode.DateTime:
110 writer.WriteString (String.Format (CultureInfo.InvariantCulture, "/Date({0})/", (long) ((DateTime) graph).Subtract (new DateTime (1970, 1, 1)).TotalMilliseconds));
114 goto case TypeCode.String;
115 } else if (graph is Uri) {
116 goto case TypeCode.String;
117 } else if (graph is XmlQualifiedName) {
118 XmlQualifiedName qn = (XmlQualifiedName) graph;
119 writer.WriteString (qn.Name);
120 writer.WriteString (":");
121 writer.WriteString (qn.Namespace);
122 } else if (TypeMap.IsDictionary (type)) {
123 writer.WriteAttributeString ("type", "array");
124 var itemGetter = type.GetProperty ("Item");
125 var keysGetter = type.GetProperty ("Keys");
126 var argarr = new object [1];
127 foreach (object o in (IEnumerable) keysGetter.GetValue (graph, null)) {
128 writer.WriteStartElement ("item");
129 writer.WriteAttributeString ("type", "object");
130 // outputting a KeyValuePair as <Key .. /><Value ... />
131 writer.WriteStartElement ("Key");
132 WriteObjectContent (o, false, !(graph is Array && type.GetElementType () != typeof (object)));
133 writer.WriteEndElement ();
134 writer.WriteStartElement ("Value");
136 WriteObjectContent (itemGetter.GetValue (graph, argarr), false, !(graph is Array && type.GetElementType () != typeof (object)));
137 writer.WriteEndElement ();
138 writer.WriteEndElement ();
140 } else if (graph is Array || TypeMap.IsEnumerable (type)) {
141 writer.WriteAttributeString ("type", "array");
142 foreach (object o in (IEnumerable) graph) {
143 writer.WriteStartElement ("item");
144 // when it is typed, then no need to output "__type"
145 WriteObjectContent (o, false, !(graph is Array && type.GetElementType () != typeof (object)));
146 writer.WriteEndElement ();
149 TypeMap tm = GetTypeMap (type);
151 // FIXME: I'm not sure how it is determined whether __type is written or not...
152 if (outputTypeName || always_emit_type)
153 writer.WriteAttributeString ("__type", FormatTypeName (type));
154 tm.Serialize (this, graph, "object");
157 // it does not emit type="object" (as the graph is regarded as a string)
158 // writer.WriteString (graph.ToString ());
159 throw new InvalidDataContractException (String.Format ("Type {0} cannot be serialized by this JSON serializer", type));
165 string FormatTypeName (Type type)
167 return String.Format ("{0}:#{1}", type.Name, type.Namespace);
170 TypeMap GetTypeMap (Type type)
173 if (!typemaps.TryGetValue (type, out map)) {
174 map = TypeMap.CreateTypeMap (type);
175 typemaps [type] = map;