Function opt
Allows safe access of sub paths of a JSONValue
.
Missing intermediate values will not cause an error, but will instead just cause the final path node to be marked as non-existent. See the example below for the possbile use cases.
Example
import std .exception : assertThrown;
JSONValue subobj = ["b": JSONValue(1.0), "c": JSONValue(2.0)];
JSONValue subarr = [JSONValue(3.0), JSONValue(4.0), JSONValue(null)];
JSONValue obj = ["a": subobj, "b": subarr];
// access nested fields using member access syntax
assert(opt(obj) .a .b == 1.0);
assert(opt(obj) .a .c == 2.0);
// get can be used with a default value
assert(opt(obj) .a .c .get(-1.0) == 2.0); // matched path and type
assert(opt(obj) .a .c .get(null) == null); // mismatched type -> return default value
assert(opt(obj) .a .d .get(-1.0) == -1.0); // mismatched path -> return default value
// explicit existence check
assert(!opt(obj) .x .exists);
assert(!opt(obj) .a .x .y .exists); // works for nested missing paths, too
// instead of using member syntax, index syntax can be used
assert(opt(obj)["a"]["b"] == 1.0);
// integer indices work, too
assert(opt(obj) .b[0] == 3.0);
assert(opt(obj) .b[1] == 4.0);
assert(opt(obj) .b[2] .exists);
assert(opt(obj) .b[2] == null);
assert(!opt(obj) .b[3] .exists);
// accessing a missing path throws an exception
assertThrown(opt(obj) .b[3] == 3);
// assignments work, too
opt(obj) .b[0] = 12;
assert(opt(obj) .b[0] == 12);
// assignments to non-existent paths automatically create all missing parents
opt(obj) .c .d .opDispatch!"e"( 12);
assert(opt(obj) .c .d .e == 12);
// writing to paths with conflicting types will throw
assertThrown(opt(obj) .c[2] = 12);
// writing out of array bounds will also throw
assertThrown(opt(obj) .b[10] = 12);