2025-09-20 22:59:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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') {
|
2025-09-21 00:45:34 +02:00
|
|
|
if (toks.length === 0) {
|
|
|
|
|
ptr[tok] = value;
|
|
|
|
|
return json;
|
|
|
|
|
} else {
|
|
|
|
|
ptr[tok] = {};
|
|
|
|
|
ptr = ptr[tok];
|
|
|
|
|
}
|
2025-09-20 22:59:18 +02:00
|
|
|
} 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,
|
|
|
|
|
};
|