DateTime values appear as JSON strings in the form of "/Date(700000+0500)/", where the first number (700000 in the example provided) is the number of milliseconds in the GMT time zone, regular (non-daylight savings) time since midnight, January 1, 1970. The number may be negative to represent earlier times. The part that consists of "+0500" in the example is optional and indicates that the time is of the Local kind - that is, should be converted to the local time zone on deserialization. If it is absent, the time is deserialized as Utc. The actual number ("0500" in this example) and its sign (+ or -) are ignored.
When serializing DateTime, Local and Unspecified times are written with an offset, and Utc is written without.
The ASP.NET AJAX client JavaScript code automatically converts such strings into JavaScript DateTime instances. If there are other strings that have a similar form that are not of type DateTime in .NET, they are converted as well.
The conversion only takes place if the "/" characters are escaped (that is, the JSON looks like "\/Date(700000+0500)\/"), and for this reason WCF's JSON encoder (enabled by the WebHttpBinding) always escapes the "/" character.
XmlElement
XmlElement is serialized as is, with no wrapping. For example, data member "x" of type XmlElement that contains <abc/> is as represented as follows.
Arrays of XmlNode
Array objects of type XmlNode are wrapped in an element called ArrayOfXmlNode in the standard data contract namespace for the type. If "x" is an array that contains attribute node "N" in namespace "ns" that contains "value" and an empty element node "M", the representation is as follows.
{"x":"<ArrayOfXmlNode xmlns=\"http://schemas.datacontract.org/2004/07/System.Xml\" a:N=\"value\" xmlns:a=\"ns\"><M/></ArrayOfXmlNode>"}
Attributes in the empty namespace at the beginning of XmlNode arrays (before other elements) are unsupported.
IXmlSerializable Types including XElement and DataSet
ISerializable types subdivide into "content types", "DataSet types" and "element types". For definitions of these types, see XML and ADO.NET Types in Data Contracts.
"Content" and "DataSet" types are serialized similar to Array objects of XmlNode discussed in the previous section. They are wrapped in an element whose name and namespace corresponds to the data contract name and namespace of the type in question.
"Element" types such as XElement are serialized as is, similar to XmlElement previously discussed in this topic.
Preserving Type Information
As stated earlier, polymorphism is supported in JSON with some limitations. Javascript is a weakly-typed language and type identity is normally not an issue. However, when using JSON to communicate between a strongly-typed system (.NET) and a weakly-typed system (Javascript), it is useful to preserve type identity. For example, types with data contract names "Square" and "Circle" derive from a type with a data contract name of "Shape". If "Circle" is sent from .NET to Javascript and is later returned to a .NET method that expects "Shape", it is useful for the .NET side to know that the object in question was originally a "Circle" - otherwise any information specific to the derived type (for example, "radius" data member on "Circle") may be lost.
To preserve type identity, when serializing complex types to JSON a "type hint" can be added, and the deserializer recognizes the hint and acts appropriately. The "type hint" is a JSON key/value pair with the key name of "__type" (two underscores followed by the word "type"). The value is a JSON string of the form "DataContractName:DataContractNamespace" (anything up to the first colon is the name). Using the earlier example, "Circle" can be serialized as follows.
{"__type":"Circle:http://example.com/myNamespace","x":50,"y":70,"radius":10}
The type hint is very similar to the xsi:type attribute defined by the XML Schema Instance standard and used when serializing/deserializing XML.
Data members called "__type" are forbidden due to potential conflict with the type hint.
Reducing the Size of Type Hints
To reduce the size of JSON messages, the default data contract namespace prefix (http://schemas.datacontract.org/2004/07/) is replaced with the "#" character. (To make this replacement reversible, an escaping rule is used: if the namespace starts with the "#" or "\" characters, they are appended with an extra "\" character). Thus, if "Circle" is a type in the .NET namespace "MyApp.Shapes", its default data contract namespace is http://schemas.datacontract.org/2004/07/MyApp. Shapes and the JSON representation is as follows.
{"__type":"Circle:#MyApp.Shapes","x":50,"y":70,"radius":10}
Both the truncated (#MyApp.Shapes) and the full (http://schemas.datacontract.org/2004/07/MyApp.Shapes) names is understood on deserialization.
Type Hint Position in JSON Objects
Note that the type hint must appear first in the JSON representation. This is the only case where order of key/value pairs is important in JSON processing. For example, the following is not a valid way to specify the type hint.
{"x":50,"y":70,"radius":10,"__type":"Circle:#MyApp.Shapes"}
Both the DataContractJsonSerializer used by WCF and ASP.NET AJAX client pages always emit the type hint first.
Type Hints Apply Only to Complex Types
There is no way to emit a type hint for non-complex types. For example, if an operation has an Object return type but returns a Circle, the JSON representation can be as shown earlier and the type information is preserved. However, if Uri is returned, the JSON representation is a string and the fact that the string used to represent a Uri is lost. This applies not only to primitive types but also to collections and arrays.
When Are Type Hints Emitted
Type hints may increase message size significantly (one way to mitigate this is to use shorter data contract namespaces if possible). Therefore, the following rules govern whether type hints are emitted:
-
When using ASP.NET AJAX, type hints are always emitted whenever possible, even if there is no base/derived assignment - for example, even if a Circle is assigned to a Circle. (This is required to fully enable the process of calling from the weakly-typed JSON environment into the strongly-typed .NET environment with no surprising loss of information.)
-
When using AJAX services with no ASP.NET integration, type hints are only emitted when there is a base/derived assignment - that is, emitted when Circle is assigned to Shape or Object but not when assigned to Circle. This provides the minimum information required to correctly implement a Javascript client, thus improving performance, but does not protect against type information loss in incorrectly-designed clients. Avoid base/derived assignments altogether on the server if you want to avoid dealing with this issue on the client.
-
When using the DataContractSerializer type, the alwaysEmitTypeInformation constructor parameter allows you to choose between the preceding two modes, with the default being "false" (only emit type hints when required).
Duplicate Data Member Names
Derived type information is present in the same JSON object together with base type information, and can occur in any order. For example, Shape may be represented as follows.
{"__type":"Shape:#MyApp.Shapes","x":50,"y":70}
Whereas Circle may be represented as follows.
{"__type":"Circle:#MyApp.Shapes","x":50, "radius":10,"y":70}
If the base Shape type also contained a data member called "radius", this leads to a collision on both serialization (because JSON objects cannot have repeating key names) and deserialization (because it is unclear whether "radius" refers to Shape.radius or Circle.radius). Therefore, while the concept of "property hiding" (data members of the same name on based and derived classes) is generally not recommended in data contract classes, it is actually forbidden in the case of JSON.
Polymorphism and IXmlSerializable Types
IXmlSerializable types may be polymorphically assigned to each other as usual as long as Known Types requirements are met, according to usual data contract rules. However, serializing an IXmlSerializable type in place of Object results in loss of type information as the result is a JSON string.
Polymorphism and Certain Interface Types
It is forbidden to serialize a collection type or a type that implements IXmlSerializable where a non-collection type that is not IXmlSerializable (except for Object) is expected. For example, a custom interface called IMyInterface and a type MyType that implement both IEnumerable of type int and IMyInterface. It is forbidden to return MyType from an operation whose return type is IMyInterface. This is because MyType must be serialized as a JSON array and requires a type hint, and as stated before you cannot include a type hint with arrays, only with complex types.
Known Types and Configuration
All of the Known Type mechanisms used by the DataContractSerializer are also supported in the same way by the DataContractJsonSerializer. Both serializers read the same configuration element, <dataContractSerializer> in <system.runtime.serialization>, to discover known types added through a configuration file.
Collections Assigned to Object
Collections assigned to Object are serialized as if they are collections that implement IEnumerable: a JSON array with each entry that has a type hint if it is a complex type. For example, a List of type Shape assigned to Object looks like the following.
[{"__type":"Shape:#MyApp.Shapes","x":50,"y":70},
{"__type":"Shape:#MyApp.Shapes","x":58,"y":73},
{"__type":"Shape:#MyApp.Shapes","x":41,"y":32}]
When deserialized back into Object:
-
Shape must be in the Known Types list. Having List of type Shape in known types has no effect. Note that you do not have to add Shape to known types on serialization in this case - this is done automatically.
-
The collection is deserialized as an Array of type Object that contains Shape instances.
Derived Collections Assigned to Base Collections
When a derived collection is assigned to a base collection, the collection is usually serialized as if it was a collection of the base type. However, if the item type of the derived collection cannot be assigned to the item type of the base collection, an exception is thrown.
Type Hints and Dictionaries
When a dictionary is assigned to an Object, each Key and Value entry in the dictionary is treated as if it was assigned to Object and gets a type hint.
When serializing dictionary types, the JSON object that contains the "Key" and "Value" members is unaffected by the alwaysEmitTypeInformation setting and only contains a type hint when the preceding collection rules require it.