Fast .NET JSON (De)Serializer, Built On Sigil
A fast JSON (de)serializer, built on Sigil with a number of somewhat crazy optimization tricks.
Releases are available on Nuget in addition to this repository.
using(var output = new StringWriter())
{
JSON.Serialize(
new
{
MyInt = 1,
MyString = "hello world",
// etc.
},
output
);
}There is also a Serialize method that returns a string.
The first time Jil is used to serialize a given configuration and type pair, it will spend extra time building the serializer. Subsequent invocations will be much faster, so if a consistently fast runtime is necessary in your code you may want to "prime the pump" with an earlier "throw away" serialization.
If you need to serialize compile-time unknown types (including subclasses, and virtual properties) you should use JSON.SerializeDynamic instead.
JSON.SerializeDynamic does not require a generic type parameter, and can cope with subclasses, object/dynamic members, and DLR participating
types such as ExpandoObject and DynamicObject.
using(var input = new StringReader(myString))
{
var result = JSON.Deserialize<MyType>(input);
}There is also a Deserialize method that takes a string as input.
The first time Jil is used to deserialize a given configuration and type pair, it will spend extra time building the deserializer. Subsequent invocations will be much faster, so if a consistently fast runtime is necessary in your code you may want to "prime the pump" with an earlier "throw away" deserialization.
Jil is case sensitive as a rule, so when deserializing make sure your member names match what is in your JSON.
using(var input = new StringReader(myString))
{
var result = JSON.DeserializeDynamic(input);
}There is also a DeserializeDynamic method that works directly on strings.
These methods return dynamic, and support the following operations:
(int)JSON.DeserializeDynamic("123")JSON.DeserializeDynamic(@"{""A"":123}").AJSON.DeserializeDynamic(@"{""A"":123}")["A"]JSON.DeserializeDynamic("[0, 1, 2]")[0]foreach(var keyValue in JSON.DeserializeDynamic(@"{""A"":123}")) { ... }keyValue is a dynamic with Key and Value propertiesforeach(var item in JSON.DeserializeDynamic("[0, 1, 2]")) { ... }item is a dynamic and will have values 0, 1, and 2.Length & .Count on arrays.ContainsKey(string) on objectsJil will only (de)serialize types that can be reasonably represented as JSON.
The following types (and any user defined types composed of them) are supported:
Jil deserializes public fields and properties; the order in which they are serialized is not defined (it is unlikely to be in
declaration order). The DataMemberAttribute.Name property and IgnoreDataMemberAttribute are respected by Jil, as is the ShouldSerializeXXX() pattern. For situations where DataMemberAttribute and IgnoreDataMemberAttribute cannot be used, Jil provides the JilDirectiveAttribute which provides equivalent functionality.
Strong typing of primitives types (int, long, etc.) can be done by annotating a wrapper type with [JilPrimitiveWrapper]. Such a type should have one declared field or property, and default or single parameter constructor.
Jil has limited support for "unions" (fields on JSON objects that may contain one of several types), provided that they can be distiguished by their first character.
In other words:
class LegalUnion
{
[JilDirective(Name = "Foo", IsUnion = true)]
public string FooString { get; set; }
[JilDirective(Name = "Foo", IsUnion = true)]
public int FooInt { get; set; }
}Is allowed because the first character of a JSON string is always ", while the first character of a JSON number is a digit or -.
The following would not be legal, however.
class IllegalUnion
{
[JilDirective(Name = "Foo", IsUnion = true)]
public uint FooUInt { get; set; }
[JilDirective(Name = "Foo", IsUnion = true)]
public double FooDouble { get; set; }
}Since both properties could start with a digit.
You can also use a Type member to determine which field was (de)serialized.
class WithUnionType
{
[JilDirective(Name = "Foo", IsUnion = true, IsUnionType = true)]
public Type FooType { get; set; }
[JilDirective(Name = "Foo", IsUnion = true)]
public uint FooUInt { get; set; }
[JilDirective(Name = "Foo", IsUnion = true)]
public List<int> FooList { get; set; }
}When serializing this field must be set.
Jil's JSON.Serialize and JSON.Deserialize methods take an optional Options parameter which controls:
[MemberName] or [JilDirective]"CamelCase" would become "camelCase"Jil aims to be the fastest general purpose JSON (de)serializer for .NET. Flexibility and "nice to have" features are explicitly discounted in the pursuit of speed.
These benchmarks were run on a machine with the following specs:
As with all benchmarks, take these with a grain of salt.
For comparison, here's how Jil stacks up against other popular .NET serializers in a synthetic benchmark:
All three libraries are in use at Stack Exchange in various production roles.
Note that the bars in each group of each graph are scaled so that the fastest library is 100.
Numbers, include millisecond timings, can found in this Google Document.
The Question, Answer, and User types are taken from the Stack Exchange API.
Data for each type is randomly generated from a fixed seed. Random text is biased towards ASCII*, but includes all unicode.
*This is meant to simulate typical content from the Stack Exchange API.
The same libraries and same types were used to test deserialization.
Note that the bars in each group of each graph are scaled so that the fastest library is 100.
Numbers, include millisecond timings, can be found in the same Google Document.
Jil has a lot of tricks to make it fast. These may be interesting, even if Jil itself is too limited for your use.
Jil does a lot of IL generation to produce tight, focused code. While possible with ILGenerator, Jil instead uses the Sigil library. Sigil automatically does a lot of the busy work you'd normally have to do manually to produce ideal IL. Using Sigil also makes hacking on Jil much more productive, as debuging IL generation without it is pretty slow going.
Jil's internal serializers and deserial
No open issues yet, or sync has not completed.