Note 1: type extensions must be enabled and present in the jsin string for this feature to work (otherwise the deserializer cannot determine, which kind of object to create.Note 2: there is also a generic version of that method

Option 2: Sniff'n go

However, A-FastJson comes with another deserializer, the JsonValueDeserializer which returns a value store based on IJsonValue. This operation is a lot faster than the ReadObject method shown above, but is not that strongly typed. Reading into a json value can be accomplished by

The JsonObject class is essentially a dictionary which allows you to sniff into its values before continuing the deserialization process. This is for instance useful for protocol validation since it allows you to trash the json request before full deserialization has happened which saves time and increases potentially your i/o (ReadJsonValueis almost twice as fast than ReadObject, see below).

An instance of JsonObject can be used to populate an existing instance of your CLR objects. For that, use the BuilUp method on the Json singleton:

For optimal performance, you should pool your target objects; The BuildUp method is supposed to ensure that you can recycle your instances.

Configuration

By default, the Json class instance uses the default parameters for serialization and deserialization. Due to internal mechanisms, these configuration objects are expensive in contrast to those in fastJSON. This is because all serialization meta-information is attached to it.

The parameter names should be self explicit and are explained briefly in code. Since v0.93, there is a clear separation of parameters which refer to serialization and deserialization. Properties on the JsonParameters class refer to both operations. UseGlobalTypes is obsolete.

Inspecting the configuration

For debugging or automated protocol checks, one may want to see the list of properties which are visible
to the JSON (de-)serializer. For that, you can use the GetSerializationMembers method.

// For the default parameter
Apolyton.FastJson.Json.Current.GetSerializationMembers(typeof(MyClass));
or
// For any parameter object
new JsomParameter().GetSerializationMembers(typeof(MyClass);

Custom Type Support

The custom type support of FastJSON has been extended and reviewed in order to be platform independent. In essence, a parameter object can register a serialization and deserialization handler which are plain delegates. This can happen for instance through:

The submitted inline-delegate is called for each instance of the given type. Note that you cannot register a custom type handler for internal types. If you try so, an ArgumentException is thrown by the register method.

For more maintainable code, one can also implement the ICustomTypeSerializer interface and register that instance as a custom type handler:

There is already a base class, namely CustomTypeSerializer, which pre-implements that interface and lets us focus on the important part of our code (and not the formal one):

That example serializes Mongo ObjectId structs into strings and the way back. Note that we didn't implement TypeName which can define the name of the type (see Type Descriptors and Polymorphism); returning null leads to default behavior (recommended).

Type Descriptors and Polymorphism

This chapter is mostly relevant for advanced deserialization scenarios. Serializing objects works normally without need of interaction or configuration. The limits of configuration-free deserialization is reached, when polymorphic objects are used.

Understanding the problem

A simple scenario involving polymorph objects is a list of animals in which each item can be some concrete sort of animal, like dog or cat. Without type descriptors, the deserializer would deserialize each item into an animal. So given a list of 3 animals, 2 dogs and one cat, the serializer would do its job properly by writing a json array with 3 items and their properties within:

However, the deserializer just sees a list of 3 animals. Given the text information above, the type of line is lost. Therefore, the deserliazed list would only contain a list with 3 Animals ignoring all additional information in the stream. A solution to this problem is to serialize the type information as well, normally into the special property named $type:

The good about this string is, that within the .NET world it should work nicely, the bad thing about these type names are their portability and length. Obviously, the question 'What is a good type name for a user type?' cannot be answered by this toolkit. Although the recommendation is to use the DataContractTypeDescriptor, the type description can be custom by implementing an own sub-class of JsonTypeDescriptor and register it to the parameters:

As mentioned above, the JsonTypeDescriptor generates assembly qualified type names. It is easy to imagine (and implement) type descriptors which use the FullName of a type or just the Name. However, the recommended way is to use the DataContractTypeDescriptor:

As its name indicates, the descriptor is using the DataContractAttribute to determine the name of the type. As a fallback mechanism, the FullName of the type is used (if the given type does not have a DataContract attribute on it or it has no Name assigned). Assigning a name to one of our classes is straight forward:

Points of Interest

The IJsonValue concept is inspired from the JSON API in Silverlight done by Microsoft. I found it very useful to sniff into incoming requests and trash them when they are not following basic expectations (mandatory fields missing). Running a full deserialization in order to do the samewas appearing to me as a waste of resources.

The code is covered by unit tests (around 160+) which should ensure high quality of each release.

Avoid using internal properties or members in (de-) serialization. The framework reacts partially quite strangely when those properties are accessed through reflection -and in most cases, there is a negative performance impact (that's a .NET thing). Silverlight will for instance throw access violation or method not found exceptions for internal types.

Benchmarks

Before crunching some numbers, some points should be enlightened since they are easily forgotten:

All benchmarks published by framework developers, like this or fastJSON are optimized results. Real life results can look very differently. This is not bad intention, but perfectly normal. Therefore:

All results published here just performance indicators. You should compare the framework's performance in your end-to-end scenario while considering that:

Performance varies not only from run-to-run, but also from class-to-class and the data in it.

All benchmarks are run against fastJSON 2.0.13 which was the reference at publish time.

When reading benchmark results ensure that input and output are the same (*1)

(*1) For instance, the fastJSON benchmark compares with BinaryFormatter which takes a stream as input. fastJSON cannot process streams. This aspect is ignored, but it leads to a potentially wrong assumption that fastJSON is faster than BinaryFormatter..

Scenario 1: A-FastJSON vs fastJSON x86 (custom types)

(A)-FastJSON serialization is usually 20% faster than fastJSON.

(A1)-FastJSON deserialization to IJsonValue is faster than serialization and ~300% faster fastJSON.

(A2)-FastJSON deserialization to IJsonValue, then build up of into given class is almost ~20% faster than fastJSON (+10% compared to v0.91)

(A3)-FastJSON deserializatio9n
to IJsonValue, then build up of into given class with type extensions, custom type name (Data Contract support) is ~9% faster than FastJson.

(A4)-FastJSON deserialization into object is ~10% faster or equal to fastJSON

(A5)-FastJSON deserialization into a known object is ~20% faster than fastJSON (ReadObject<T>)

Scenario 2: A-FastJSON vs fastJSON x86 (custom types & exotic types)

(A)-FastJSON serialization is usually 15% faster than fastJSON.

(A1)-FastJSON deserialization to IJsonValue is faster than serialization and ~300% faster fastJSON.

(A2)-FastJSON deserialization to IJsonValue, then build up of into given class is almost ~20% faster than fastJSON (+10% compared to v0.91)

(A3)-FastJSON deserializatio9n
to IJsonValue, then build up of into given class with type extensions, custom type name (Data Contract support) is slower than FastJson.

(A4)-FastJSON deserialization into object is ~19% faster or equal to fastJSON

(A5)-FastJSON deserialization into a known object is ~15% faster than fastJSON (ReadObject<T>)

Note that 64 Bit scenarios are not listed anymore since they don't reveal surprising measures (and are therefore not meaningful).

Conclusions

Both, ApJson and fastJSON are reasonably fast.

In 64 bit scenarios the advantage of ApJson is lower.

If one needs built in data set/ data table support, fastJSON is likely the better option.

If one needs exotic type support (dictionaries, hash-sets etc), ApJson is likely the better option.

The IJsonValue conversion is surprisingly fast and seems to be a very good option -especially, if one considers that the second step, converting the generic dictionary into a given type, is optional.

Due to code optimizations, ApJson is meanwhile faster than FastJson in most of the scenarios it defines.

Known Issues and Limitations

DataSet and DataTable are not supported by the BuildUp method (*2). Anyway, the value to convert from one generic type into another one should be quite low. Given enough interest, extension methods might be added on IJsonValue.

HashSet in deserialization is not supported (lack of interface to populate the hash set collection).

DataMember with Name = "$type" and comparable is still allowed and leads potentially to wrong behavior.

InvalidProgramException is thrown when property indexer (aka this[]) is defined on class to serialize

Deserialization into non-public types fails with TypeAccessException.

UseGlobalTypes doesn't work with JsonValueDeserializer.

(*2) DataTable and DataSet can be deserialized by the
ReadDataTable/ ReadDataSet methods, however this part of the code is currently not tested.

Release Notes:

v1.0:

Small performance improvements using JsonValue deserialization.

[NEW] Can now deserialize into a HashSet<>

[NEW] Allowing to buildup json arrays.

[NEW] Deserialization into enumeration types is now trying to set an array of the items (instead of exception)

[Change] DateTimeKind specifications (local, Utc, etc) have only an implicit effect on deserialized values, if the string values is zulu time. Otherwise, the kind is set according to the option, but the value is not modified.

[Change] Type Extensions are now disabled by default as this is an advanced case and has a significant performance impact.

v0.92

First intermediate release before API stability is guaranteed (in v1.0)

Removing support of
xmlignore attribute, it is replaced by DataMember, IgnoreDataMember

New: JsonPrimitive custom type support.

New: Silverlight 5 support.

New: A
SerializationException is thrown when an attempt is done to serialize a class without (visible) properties. Previously, this was rendering to '{}' which will lead to problems on receivers side.

New: A date time property can be decorated with the DateTimeOptions attribute which defines the expected kind of date time (utc or not). The deserializer will automatically convert, if
appropriate.

Change: Thread static attribute has been removed
from JSON class. It was causing irritations, since each thread had its own default parameters. Any change applied to the parameters needed to be re-set for each worker thread.

Comments and Discussions

Upcoming Changes 0.93:- IJsonValue AsXXXX is obsolete. Use explicit cast operators instead- JsonPrimitive decimal was using integer parsing- JsonPrimitive ToChar was not throwing an exception when local value had more than one character- Setting default UseExtensions to false since it is not required in most of the cases- JsonValue Builder will become the default deserializer except for dataTable and dataset objects.- JsonParameter will be reviewed. Currently, it misses clear separation of what is serialization option, what is deserialization and what it common.