zikzak_json
A best-effort JSON parser for Dart. Uses simdjson_dart for speed on
standard JSON and falls back to json5_plus for comments, trailing commas,
unquoted keys, and numeric object keys. The caller never needs to think
about which engine is running — zikzak_json handles everything and always
returns clean raw Dart types (Map, List, String, num, bool, null).
Features
- Dual-engine architecture — simdjson for strict JSON (>3x faster), json5_plus for everything else
- Transparent fallback — feed it any JSON or JSON5, get raw Dart types back
- Fast-path heuristic — detects JSON5 features (comments, unquoted keys, trailing commas) in the first 4KB to skip simdjson when failure is predictable
- No
Json5wrapper objects — recursivetoRaw()conversion strips wrapper types so you always get plainMap/List/primitives - Extract paths — dot-notation or JSONPath extraction without full document traversal
- Thread-safe — all decode calls are independent, no mutable global state
Getting started
zikzak_json is a pure Dart package with zero Flutter dependencies.
dependencies:
zikzak_json: ^0.1.0
Usage
Basic decode
import 'package:zikzak_json/zikzak_json.dart';
// Standard JSON → simdjson (fast path)
final data = ZikZakJson.decode('{"a": 1, "b": "hello"}');
print(data['a']); // 1
// JSON5 with comments → auto-detected, json5_plus path
final config = ZikZakJson.decode('''
{
site: "lowes", // unquoted key
channel: "mobile",
timeout: 2000, // trailing comma
}
''');
print(config['site']); // "lowes"
// Numeric keys → json5_plus handles what simdjson rejects
final cats = ZikZakJson.decode('{102717: "PADLOCKS"}');
print(cats['102717']); // "PADLOCKS"
Convenience methods
final map = ZikZakJson.decodeMap('{"a": 1}'); // throws if not object
final list = ZikZakJson.decodeList('[1, 2, 3]'); // throws if not array
final str = ZikZakJson.decodeString('"hello"'); // throws if not string
Detection helpers
ZikZakJson.isJson('{"a": 1}'); // true
ZikZakJson.isJson('hello'); // false
ZikZakJson.isJson5('{a: 1}'); // true (valid JSON5, not valid JSON)
ZikZakJson.isJson5('{"a": 1}'); // false (valid JSON, not JSON5-only)
Options & engine selection
// Force a specific engine
ZikZakJson.decode(source, options: ZikZakJsonOptions(
forceEngine: ZikZakJsonEngine.json5, // skip simdjson
));
// Strict mode — throw on first failure, no fallback
ZikZakJson.decode(source, options: ZikZakJsonOptions(
strict: true,
forceEngine: ZikZakJsonEngine.simdjson,
));
// Verbose timing
ZikZakJson.decode(source, options: ZikZakJsonOptions(verbose: true));
Path extraction
final json = ZikZakJson.decode(largePayload);
final brand = ZikZakJson.get(json, 'itemList.0.product.brand');
// → "Master Lock"
To skip building the full object tree, pass extractPaths instead. Paths accept
plain dot notation, an explicit RFC 6901 pointer, or a full JSONPath
expression — and resolve identically whichever engine decoded the document:
ZikZakJson.decode(
payload,
options: ZikZakJsonOptions(extractPaths: [
'itemList.0.product.brand', // dot notation
r'$.itemList[?@.price > 10].brand', // JSONPath filter
]),
);
// → {"itemList.0.product.brand": "Master Lock",
// "$.itemList[?@.price > 10].brand": "Master Lock"}
Plain dot and pointer paths are served straight off the simdjson document
without materialising the rest of it. JSONPath expressions cannot be, so they
walk the whole tree — mixing the two kinds in one call gives up that
optimisation. Paths that match nothing yield null.
One limitation: dot notation cannot address a key containing a literal .,
since the dot reads as a nesting step. Use bracket quoting for those — it works
identically on both engines:
$.meta["a.b"] // the key "a.b"
$.meta[] // an empty key
JSONPath queries
json_path_plus is re-exported, so the same single import covers decoding and
querying — no second dependency needed:
import 'package:zikzak_json/zikzak_json.dart';
final json = ZikZakJson.decode('{"itemList":[{"brand":"Master Lock"}]}');
final brands = JSONPath.query(r'$.itemList[*].brand', json, wrap: false);
// → ["Master Lock"]
On top of JSONPath-Plus syntax (filters, native @property accessors,
slicing) you get RFC 9535 filter selectors — bare [?@.price > 10] without
parentheses, match() / search() / key() built-ins, /regex/ literals,
and a working @root. Full syntax is documented in
json_path_plus.
Upgrading to 0.3.0:
json_path_plus2.0.0 removed the publicJSONPath.cachemap. Because zikzak_json re-exports the engine, useJSONPath.cacheSize,JSONPath.isCached(path)andJSONPath.clearCache()instead.
Engine Architecture
Input String
│
├── forceEngine == simdjson ───→ simdjson_dart ──→ raw types
│
├── forceEngine == json5 ──────→ json5_plus ─────→ toRaw() ──→ raw types
│
└── auto (default)
│
├── fast-path heuristic
│ └── JSON5 indicators? ──yes──→ json5_plus
│
└── try simdjson_dart
├── success ──→ return
└── fail ─────→ json5_plus fallback
Why two engines?
| Engine | Speed | Input flexibility | Use when |
|---|---|---|---|
| simdjson | ~3-5x faster | Strict JSON only | Large payloads, APIs, guaranteed valid JSON |
| json5_plus | Slower | JSON + comments + unquoted keys + trailing commas + single quotes | config files, human-written JSON5 |
Similar packages
json5— Standard JSON5 parser, rejects numeric object keys (102717: "val")json5_plus— JSON5 parser with typed accessors and$includesupportsimdjson_dart— Raw simdjson bindings for Dart
Powered by ZikZak AI
zikzak_json is developed by ZikZak AI to serve as the JSON backbone for high-throughput price comparison, and data extraction workloads.
- 🌐 zuzu.dev
- 🐙 GitHub
- 🐛 Issue Tracker
Sponsors
Thanks to ZikZak AI for sponsoring this project!
ZikZak AI is an AI-Powered Price Comparison app that you scan barcodes, and discover amazing savings instantly. Your personal shopping assistant that never sleeps.
Licensed under the Apache License, Version 2.0.


