djson 0.9.2
A high-performance, lazy JSON parser for D.
To use this package, run the following command in your project's root directory:
Manual usage
Put the following dependency into your project's dependences section:
djson
A lazy JSON parser for the D programming language. Parses only what you access: no wasted work.
Features
- Lazy parsing: only the fields you access get parsed. Perfect for extracting a few values from large JSON payloads
- Eager mode: call
parseAll()for a fully-parsed tree when you need everything - Insertion-order preservation: object keys always iterate in the original JSON order
- Fluent access API: variadic arguments and JSON pointer paths (
/a/b/0) - Safe access:
.safe!T()returns a result with a.foundflag instead of throwing - Resumable Streaming Parsing: append data to a partial JSON object and resume parsing seamlessly
- Binding: bind D structs and classes to JSON objects and arrays
- Mutation: set values, add keys, remove branches, then serialize back to JSON
- Interop with `std.json`: convert to/from
std.json.JSONValuewhen needed - Strict JSON compliance: passes 283/283 tests from the JSONTestSuite
- Performance: faster than
std.json
Documentation
Installation
dub add djson
Quick Start
import djson;
auto json = parseJSON(`{
"name": "djson",
"version": 1,
"features": ["lazy", "fast", "safe"],
"config": {"debug": false, "maxDepth": 64}
}`);
// Access values: only the accessed fields are parsed
string name = json.get!string("name"); // "djson"
int ver = json.get!int("version"); // 1
string feat = json.get!string("features", 1); // "fast"
bool dbg = json.get!bool("config", "debug"); // false
API Reference
Parsing
// Lazy parse: returns immediately, parses on demand
auto json = parseJSON(`{"key": "value"}`);
// Eager parse: fully parses the entire tree in one pass
json.parseAll();
Partial Streaming & Resumable Parsing
DJSON supports parsing partial JSON strings from a stream. If the parser hits the end of the provided string while a value is still being read, it throws a JSONPartialException. You can then append more data and resume parsing exactly where it left off.
// Start with a partial JSON string
auto json = parseJSON(`{"hello": "world", "partial" :`);
// Accessing a completed key works
writeln(json.get!string("hello")); // "world"
// Accessing an incomplete key throws JSONPartialException
try {
json.get!string("partial");
} catch (JSONPartialException e) {
// Current chunk is incomplete, need more data
}
// Append the rest of the stream
json.appendData(`"world"}`);
// Now it works!
writeln(json.get!string("partial")); // "world"
This works by leveraging the lazy nature of the parser: DJSON only resumes parsing for the node that was actually interrupted.
Reading Values
auto json = parseJSON(`{"user": {"name": "Alice", "age": 30}, "tags": ["admin"]}`);
// Direct access
string name = json.get!string("user", "name"); // variadic path
int age = json.get!int("user", "age");
// JSON pointer syntax
string name2 = json.get!string("/user/name");
string tag = json.get!string("/tags/0");
// Safe access (never throws)
auto result = json.safe!string("user", "email");
if (result.found) {
writeln(result.value);
} else {
writeln("not found");
}
// With default fallback
string email = json.safe!string("user", "email").or("n/a");
// Operator []
auto user = json["user"];
string n = user["name"].get!string;
Checking Existence
auto json = parseJSON(`{"a": 1, "b": null, "list": [10, 20]}`);
json.has("a") // true
json.has("missing") // false
json.has("a", "sub") // false: "a" is not an object
json.has("/list/0") // true: JSON pointer
json.has("list", 1) // true: variadic with index
// Null checking
json["b"].isNull // true
json["a"].isNull // false
Iteration
// Array iteration
auto arr = parseJSON(`[10, 20, 30]`);
foreach (el; arr) {
writeln(el.get!int);
}
// Array with index
foreach (size_t i, el; arr) {
writefln("[%d] = %d", i, el.get!int);
}
// Object iteration: preserves insertion order
auto obj = parseJSON(`{"z": 1, "a": 2, "m": 3}`);
foreach (string key, val; obj) {
writefln("%s = %d", key, val.get!int);
}
// Output: z = 1, a = 2, m = 3 (insertion order, not alphabetical)
Mutation
auto json = parseJSON(`{"a": 1}`);
// Set by key
json["b"] = "hello";
json["c"] = true;
// Appending to arrays (auto-promotes primitives to arrays)
json["a"] ~= 2; // "a" became [1, 2]
// Set/Append by JSON pointer path (auto-vivifies intermediate nodes)
json.set(42, "/x/y/z");
json.append("item", "/list/tags"); // creates {"list": {"tags": ["item"]}}
// Overwrite nested objects
json["a"] = parseJSON(`{"nested": true}`);
// Remove keys and array elements
json.remove("b");
auto arr = parseJSON(`[1, 2, 3, 4, 5]`);
arr.remove(2); // removes element at index 2 → [1, 2, 4, 5]
Construction (Builders)
DJSON provides a concise way to build JSON structures manually using JSOB (JSON Object Builder) and JSAB (JSON Array Builder).
import djson;
// Build a complex JSON structure fluently
auto json = JSOB(
"name", "djson",
"version", 1,
"features", JSAB("lazy", "fast", "safe"),
"metadata", JSOB(
"author", "Andrea Fontana",
"tags", JSAB(1, 2, 3)
)
);
// Add to an existing object
json["new_key"] = "new_value";
Serialization
auto json = parseJSON(`{"b": 2, "a": 1}`);
json["c"] = 3;
// Compact
json.toJSON(); // `{"b":2,"a":1,"c":3}`
// Pretty-printed
json.toJSON(true);
// {
// "b": 2,
// "a": 1,
// "c": 3
// }
Interoperability with std.json
import std.json;
import djson;
auto json = djson.parseJSON(`{"key": "value"}`);
JSONValue stdVal = json.toStdJSON();
assert(stdVal["key"].str == "value");
JSON Binding
DJSON provides a binding system to convert between D structs/classes and JValue.
Basic Usage
You can then use fromJSON!T to create a D object from a JValue, and toJSON to create a JValue from a D object.
import djson;
struct User {
// You can also apply @JSON to the whole struct/class.
// Here we apply it to a block of fields.
@JSON {
string name;
int age;
}
// This field is not included by default
string other;
}
// Convert from JValue to struct
auto json = parseJSON(`{"name": "Alice", "age": 30}`);
User u = fromJSON!User(json);
assert(u.name == "Alice");
assert(u.age == 30);
u.age = 35;
// Convert from struct to JValue
JValue v = toJSON(u);
assert(v["name"].get!string == "Alice");
UDAs for Customization
The binding system can be customized using User Defined Attributes (UDAs):
@JSON: If applied to a struct or class, all public fields are included by default. If applied to a field, that field is included even if it's not public or the parent isn't marked with@JSON. It also supports custom paths via variadic arguments or JSON pointers (e.g.,@JSON("path", "to", "key"),@JSON("/path/to/key")).@JSONIgnore: Explicitly exclude a field from binding.@JSONOptional: If the field is missing in the JSON input,fromJSONwill not throw an exception and will leave the field with its default.initvalue. Supports the same path mapping as@JSON.@JSONPreProcess!func: Apply a custom transformation function when reading from JSON. The function must have the signatureFieldType func(JValue v).@JSONPostProcess!func: Apply a custom transformation function when writing to JSON. The function must have the signatureJValue func(FieldType v).
import djson;
import djson.binding;
import std.string : toUpper;
@JSON
struct Config {
// Rename field in JSON
@JSON("max_threads") int threads;
// Optional field with default value
@JSONOptional string host = "localhost";
// Ignore this field
@JSONIgnore string internalKey;
// Custom pre-processing (e.g., uppercase a string)
@JSONPreProcess!((v) => v.get!string.toUpper)
string category;
}
auto json = parseJSON(`{"max_threads": 8, "category": "production"}`);
Config cfg = fromJSON!Config(json);
assert(cfg.threads == 8);
assert(cfg.host == "localhost");
assert(cfg.category == "PRODUCTION");
Deep Path Binding
@JSONKey supports variadic arguments and JSON pointers to bind D fields directly to nested JSON structures. You can also bind entire sub-structs for a more organized data model.
@JSON
struct Profile {
string name;
int level;
}
struct GameData {
// Bind to a deep path using JSON Pointer
@JSON("/server/status/code")
int statusCode;
// Bind using variadic segments, including array indices
@JSON("players", 0, "name")
string firstPlayerName;
// Bind using JSON Pointer for the second player
@JSON("/players/1/name")
string secondPlayerName;
// Bind an entire substructure
@JSON
Profile leader;
}
auto json = parseJSON(`{
"server": {"status": {"code": 200}},
"leader": {"name": "Alice", "level": 10},
"players": [
{"name": "Bob", "level": 5},
{"name": "Charlie", "level": 8}
]
}`);
GameData data = fromJSON!GameData(json);
assert(data.statusCode == 200);
assert(data.firstPlayerName == "Bob");
assert(data.secondPlayerName == "Charlie");
assert(data.leader.name == "Alice");
// Serialization preserves the deep structure
JValue v = toJSON(data);
assert(v.get!int("/server/status/code") == 200);
assert(v.get!string("players", 0, "name") == "Bob");
Building & Testing
# Run unit tests
dub test
# Run with LDC2 for best performance
dub test --compiler=ldc2
# Run benchmarks (in external_tests/) against std.json
cd external_tests
./run_benchmark.d # JSONTestSuite benchmark
./run_real_bench.d # Real-world benchmark (auto-downloads data)
./run_speed_test_large.d # Large string benchmark
./run_nst_suite.d # JSON compliance test suite
./run_compare.d # Value comparison with std.json
License
MIT: see LICENSE for details.
- Registered by Andrea Fontana
- 0.9.2 released 3 days ago
- trikko/djson
- MIT
- Copyright © 2026, Andrea Fontana
- Authors:
- Dependencies:
- none
- Versions:
-
Show all 3 versions0.9.2 2026-Apr-13 0.9.1 2026-Apr-11 ~main 2026-Apr-14 - Download Stats:
-
-
0 downloads today
-
6 downloads this week
-
6 downloads this month
-
6 downloads total
-
- Score:
- 0.1
- Short URL:
- djson.dub.pm