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
.WriteString (null);
70 if (serialized_object_count
++ == serializer
.MaxItemsInObjectGraph
)
71 throw new SerializationException (String
.Format ("The object graph exceeded the maximum object count '{0}' specified in the serializer", serializer
.MaxItemsInObjectGraph
));
73 switch (Type
.GetTypeCode (graph
.GetType ())) {
76 writer
.WriteString (graph
.ToString ());
80 case TypeCode
.Decimal
:
81 writer
.WriteAttributeString ("type", "number");
82 writer
.WriteString (((IFormattable
) graph
).ToString ("R", CultureInfo
.InvariantCulture
));
92 writer
.WriteAttributeString ("type", "number");
93 if (graph
.GetType ().IsEnum
)
94 graph
= ((IConvertible
) graph
).ToType (Enum
.GetUnderlyingType (graph
.GetType ()), CultureInfo
.InvariantCulture
);
95 writer
.WriteString (((IFormattable
) graph
).ToString ("G", CultureInfo
.InvariantCulture
));
97 case TypeCode
.Boolean
:
98 writer
.WriteAttributeString ("type", "boolean");
100 writer
.WriteString ("true");
102 writer
.WriteString ("false");
104 case TypeCode
.DateTime
:
105 writer
.WriteString (String
.Format (CultureInfo
.InvariantCulture
, "/Date({0})/", (long) ((DateTime
) graph
).Subtract (new DateTime (1970, 1, 1)).TotalMilliseconds
));
109 goto case TypeCode
.String
;
110 } else if (graph
is Uri
) {
111 goto case TypeCode
.String
;
112 } else if (graph
is XmlQualifiedName
) {
113 XmlQualifiedName qn
= (XmlQualifiedName
) graph
;
114 writer
.WriteString (qn
.Name
);
115 writer
.WriteString (":");
116 writer
.WriteString (qn
.Namespace
);
117 } else if (graph
is IDictionary
) {
118 writer
.WriteAttributeString ("type", "array");
119 IDictionary dic
= (IDictionary
) graph
;
120 foreach (object o
in dic
.Keys
) {
121 writer
.WriteStartElement ("item");
122 writer
.WriteAttributeString ("type", "object");
123 // outputting a KeyValuePair as <Key .. /><Value ... />
124 writer
.WriteStartElement ("Key");
125 WriteObjectContent (o
, false, !(graph
is Array
&& graph
.GetType ().GetElementType () != typeof (object)));
126 writer
.WriteEndElement ();
127 writer
.WriteStartElement ("Value");
128 WriteObjectContent (dic
[o
], false, !(graph
is Array
&& graph
.GetType ().GetElementType () != typeof (object)));
129 writer
.WriteEndElement ();
130 writer
.WriteEndElement ();
132 } else if (graph
is ICollection
) { // array
133 writer
.WriteAttributeString ("type", "array");
134 foreach (object o
in (ICollection
) graph
) {
135 writer
.WriteStartElement ("item");
136 // when it is typed, then no need to output "__type"
137 WriteObjectContent (o
, false, !(graph
is Array
&& graph
.GetType ().GetElementType () != typeof (object)));
138 writer
.WriteEndElement ();
141 TypeMap tm
= GetTypeMap (graph
.GetType ());
143 // FIXME: I'm not sure how it is determined whether __type is written or not...
144 if (outputTypeName
|| always_emit_type
)
145 writer
.WriteAttributeString ("__type", FormatTypeName (graph
.GetType ()));
146 writer
.WriteAttributeString ("type", "object");
147 tm
.Serialize (this, graph
);
150 // it does not emit type="object" (as the graph is regarded as a string)
151 // writer.WriteString (graph.ToString ());
152 throw new InvalidDataContractException (String
.Format ("Type {0} cannot be serialized by this JSON serializer", graph
.GetType ()));
158 string FormatTypeName (Type type
)
160 return String
.Format ("{0}:#{1}", type
.Name
, type
.Namespace
);
163 TypeMap
GetTypeMap (Type type
)
166 if (!typemaps
.TryGetValue (type
, out map
)) {
167 map
= TypeMap
.CreateTypeMap (type
);
168 typemaps
[type
] = map
;