Files
jsl/json.js
2025-09-20 22:59:18 +02:00

143 lines
4.0 KiB
JavaScript

function jsonQuery(json, path, defaultValue = null) {
let toks = tokenizePath(path);
let ptr = json;
let search = null;
let searchTok = null;
let multiResult = null;
let tok;
while (tok = toks.shift()) {
switch (tok) {
case ".": // find key
search = 'key';
break;
case "[": // find array index
search = "array";
break;
case "]": // find
// if searchTok is "", match all items
if (search !== "array") {
throw new Error("Invalid path");
}
if (searchTok === null) {
if (multiResult !== null) {
throw new Error("Can't match full arrays on multiple levels");
}
multiResult = [];
ptr.forEach(item => {
let res = jsonQuery(item, toks.join(""));
multiResult.push(res);
});
return multiResult;
}
if (typeof ptr[searchTok] === 'undefined') {
return defaultValue;
}
ptr = ptr[searchTok];
break;
default:
if (search === 'key') {
// find key tok in ptr
if (typeof ptr[tok] === 'undefined') {
return defaultValue;
} else {
ptr = ptr[tok];
}
} else if (search === 'array') {
// find index tok in ptr after finding a ]
searchTok = tok;
}
}
}
return ptr;
}
function jsonPatch(json, path, value) {
let toks = tokenizePath(path);
json = structuredClone(json);
let ptr = json;
let search = null;
let searchTok = null;
let multiResult = null;
let tok;
while (tok = toks.shift()) {
switch (tok) {
case ".": // find key
search = 'key';
break;
case "[": // find array index
search = "array";
break;
case "]": // find
// if searchTok is "", match all items
if (search !== "array") {
throw new Error("Invalid path");
}
if (searchTok === null) {
throw new Error("Can't patch arrays yet");
}
if (typeof ptr[searchTok] === 'undefined') {
return json;
}
ptr = ptr[searchTok];
break;
default:
if (search === 'key') {
// find key tok in ptr
if (typeof ptr[tok] === 'undefined') {
return defaultValue;
} else {
if (toks.length === 0) {
ptr[tok] = value;
return json;
} else {
ptr = ptr[tok];
}
}
} else if (search === 'array') {
// find index tok in ptr after finding a ]
searchTok = tok;
}
}
}
// ptr = value;
return json;
}
function tokenizePath(path) {
let t = [];
let b = null;
for (let p = 0; p < path.length; p++) {
let c = path[p];
if (c == ".") {
if (b !== null) t.push(b);
b = null;
t.push(".");
} else if (c == "[") {
if (b !== null) t.push(b);
b = null;
t.push("[");
} else if (c == "]") {
if (b !== null) t.push(b);
b = null;
t.push("]");
} else {
b = ((b===null)?"":b) + c;
}
}
if (b !== null) t.push(b);
return t;
}
export {
tokenizePath,
jsonQuery,
jsonPatch,
};