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 ())) {
75 writer
.WriteString (graph
.ToString ());
79 case TypeCode
.Decimal
:
80 writer
.WriteAttributeString ("type", "number");
81 writer
.WriteString (((IFormattable
) graph
).ToString ("R", CultureInfo
.InvariantCulture
));
91 writer
.WriteAttributeString ("type", "number");
92 if (graph
.GetType ().IsEnum
)
93 graph
= ((IConvertible
) graph
).ToType (Enum
.GetUnderlyingType (graph
.GetType ()), CultureInfo
.InvariantCulture
);
94 writer
.WriteString (((IFormattable
) graph
).ToString ("G", CultureInfo
.InvariantCulture
));
96 case TypeCode
.Boolean
:
97 writer
.WriteAttributeString ("type", "boolean");
99 writer
.WriteString ("true");
101 writer
.WriteString ("false");
103 case TypeCode
.DateTime
:
104 writer
.WriteString (String
.Format (CultureInfo
.InvariantCulture
, "/Date({0})/", ((DateTime
) graph
).Subtract (new DateTime (1970, 1, 1)).TotalMilliseconds
));
108 goto case TypeCode
.String
;
109 } else if (graph
is Uri
) {
110 goto case TypeCode
.String
;
111 } else if (graph
is XmlQualifiedName
) {
112 XmlQualifiedName qn
= (XmlQualifiedName
) graph
;
113 writer
.WriteString (qn
.Name
);
114 writer
.WriteString (":");
115 writer
.WriteString (qn
.Namespace
);
116 } else if (graph
is ICollection
) { // array
117 writer
.WriteAttributeString ("type", "array");
118 foreach (object o
in (ICollection
) graph
) {
119 writer
.WriteStartElement ("item");
120 // when it is typed, then no need to output "__type"
121 WriteObjectContent (o
, false, !(graph
is Array
&& graph
.GetType ().GetElementType () != typeof (object)));
122 writer
.WriteEndElement ();
125 TypeMap tm
= GetTypeMap (graph
.GetType ());
127 // FIXME: I'm not sure how it is determined whether __type is written or not...
128 if (outputTypeName
|| always_emit_type
)
129 writer
.WriteAttributeString ("__type", FormatTypeName (graph
.GetType ()));
130 writer
.WriteAttributeString ("type", "object");
131 tm
.Serialize (this, graph
);
134 // it does not emit type="object" (as the graph is regarded as a string)
135 // writer.WriteString (graph.ToString ());
136 throw new InvalidDataContractException (String
.Format ("Type {0} cannot be serialized by this JSON serializer", graph
.GetType ()));
142 string FormatTypeName (Type type
)
144 return String
.Format ("{0}:#{1}", type
.Name
, type
.Namespace
);
147 TypeMap
GetTypeMap (Type type
)
150 if (!typemaps
.TryGetValue (type
, out map
)) {
151 map
= TypeMap
.CreateTypeMap (type
);
152 typemaps
[type
] = map
;