This is a .NET Standard 2.0 library providing a lightweight dynamic wrapper for super-neat, fast and low-allocating working with JSON, based on the new System.Text.Json
.
This library is especially useful for prototyping and scripting when you have to keep the code as clean and easy-to-read as possible.
PM> Install-Package Dynamic.Json
// parse json from string/stream/etc, for example
var json = DJson.Parse(@"
{
""versionNumber"": 1,
""product_name"": ""qwerty"",
""items"": [ 1, 2, 3 ],
""settings"": {
""enabled"": false
}
}");
// or read json from file
var json = DJson.Read("file.json");
// or get json from HTTP
var json = await DJson.GetAsync("https://api.com/endpoint");
// or use HttpClient extension
var json = await httpClient.GetJsonAsync("https://api.com/endpoint");
You can access json props either like class properties (json.someProperty
) or like dictionary elements (json["someProperty"]
). Both methods are identical, however, the first one looks better :)
int version = json.versionNumber;
string name = json.product_name;
int item = json.items[1];
int length = json.items.length; //or json.items.count, as you prefer
bool enabled = json.settings.enabled;
camelCase
and snake_case
are common naming styles in JSON, but uncommon in C# where we use PascalCase
for public properties. Therefore, for perfectionists like me DJson
allows to access json props not only by their original names (e.g. json.ip_endpoint
), but also by its PascalCase version (e.g. json.IPEndpoint
). DJson
will try to resolve names automatically.
var val = json.VersionNumber;
var str = json.ProductName;
var arr = json.Items[1];
var len = json.Items.Length; //or .Count
var obj = json.Settings.Enabled;
You can enumerate json arrays in foreach
or by implicit conversion to IEnumerable<dynamic>
.
foreach (var item in json.Items)
Console.WriteLine($"{item}");
var items = (IEnumerable<dynamic>)json.Items;
var sum = items.Where(x => x > 1).Sum(x => x);
You can enumerate json object props (string Name, dynamic Value)
in foreach
or by implicit conversion to IEnumerable<dynamic>
.
foreach (var prop in json.Settings)
Console.WriteLine($"{prop.Name}: {prop.Value}");
var props = (IEnumerable<dynamic>)json.Settings;
var names = props.Where(x => !x.Value).Select(x => x.Name);
While conversion to all built-in C# types is pretty transparent, you can also serialize json to any other type using implicit conversion.
var list = (List<int>)json.Items; // dynamic array -> List<int>
var settings = (MyClass)json.Settings; // dynamic object -> MyClass
var obj = (AnotherType)json;
People often use Unix time format in JSON and that's quite annoying to convert it to DateTime
, so DJson
does this for you automatically.
var json = DJson.Parse(@"
{
""time"": ""2020-09-23T21:12:16Z"",
""unix_time"": 1600895536,
""unix_time_ms"": 1600895536123
}");
DateTime time = json.Time;
DateTime unixTime = json.UnixTime; // DJson automatically detects if it's Unix time in seconds
DateTime unixTimeMs = json.UnixTimeMs; // or if it's Unix time in milliseconds
One can think that using dynamic wrapper brings a huge overhead, but it's actually not. Here is a simple benchmark, comparing DJson
with JsonDocument
from System.Text.Json
and JToken
from Newtonsoft.Json
:
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated |
|--------------- |---------:|----------:|----------:|------:|--------:|-------:|----------:|
| SystemTextJson | 3.046 us | 0.0590 us | 0.0655 us | 1.00 | 0.00 | 0.1945 | 1.21 KB |
| DJson | 4.111 us | 0.0308 us | 0.0257 us | 1.35 | 0.03 | 0.5722 | 3.53 KB |
| Newtonsoft | 7.003 us | 0.1357 us | 0.1946 us | 2.29 | 0.09 | 1.4496 | 8.91 KB |
Of course, using .Deserialize<T>()
would be even faster, but we're only talking about dynamic-like access, when you will likely spend a lot more time describing the types do deserialize your JSON to.
Feel free to create issues, feature requests and pull requests.
Cheers 🍻