Позиция в строке JSON для пути в объекте
У меня есть строка JSON, подобная этой:
{
"Version": "XXX",
"Statements": [
{...},
{...},
{...}
]
}
Как узнать, какой объект внутри свойства Statements определен в символе XX
строки JSON? (учитывая, что эти объекты могут иметь произвольно глубокое вложение).
Например, если у меня есть строка
{"Version":"XXX","Statements":[{"a":1},{"b":2},{"b":3}]}
--------------------------------------------------------
123456789 123456789 123456789 123456789 123456789 123456
то символ в позиции 36
будет соответствовать первому объекту оператора, тогда как символ в позиции 52
будет соответствовать третьему объекту оператора.
Ответы
Ответ 1
После нескольких исследований я думаю, что у меня есть путь вперед, не написав собственный парсер, используя пакет esprima
. Поскольку esprima это не JSON специфический (но скорее JavaScript), я должен обернуть свою строку JSON в скобки.
Каждый элемент дерева содержит свойство loc
с диапазоном, соответствующим ему, в позицию в исходной строке JSON.
var esprima = require("esprima");
var JSONPath = require('JSONPath');
function getStatementIndex(str, line, column) {
var tree = esprima.parseScript(str, {loc:true});
var query = "$.body[0].expression.properties[?(@.key.value=='Statement')].value.elements[*].loc";
var locations = JSONPath({json: tree, path: query});
console.log(locations);
for(var i = 0; i < locations.length; i++) {
var loc = locations[i];
var contains = false;
if (loc.start.line < line && loc.end.line > line) {
continue;
}
// If a single line and in between
if (loc.start.line == loc.end.line && loc.start.line == line) {
if (loc.start.column <= column && loc.end.column >= column) {
contains = true;
}
// If on the beginning line
} else if (loc.start.line == line && loc.start.column <= column) {
contains = true;
// If on the end line
} else if (loc.end.line == line && loc.end.column >= column) {
contains = true;
// If in between
} else if (loc.start.line < line && loc.end.line > line) {
contains = true;
}
if (contains)
return i;
}
return -1;
}
var result = getStatementIndex(str, 81, 7);
Ответ 2
Вот какое-то грязное решение, которое не требует внешних библиотек:
const data = '{"Version":"XXX","Statements":[{"a":1},{"b":2},{"b":3}],"some":0}';
const getValuesPositionInArray = arrayKey => data => {
const arrayNameSeparator = '"${arrayKey}":';
const targetArrayIndexOf = data.indexOf(arrayNameSeparator) + arrayNameSeparator.length;
const arrayStringWithRest = data.slice(targetArrayIndexOf, data.length);
const { result } = arrayStringWithRest.split('').reduce(
(acc, char, idx, array) => {
if (acc.finished) return acc;
if (!acc.processingKey && char === '[') acc.nesting += 1;
if (!acc.processingKey && char === ']') acc.nesting -= 1;
const shouldFinish = acc.nesting === 0;
const charIsDblQuote = char === '"';
const charBefore = array[idx - 1];
const charAfter = array[idx + 1];
acc.position += 1;
acc.finished = shouldFinish;
if (acc.processingKey && !charIsDblQuote) acc.processedKey += char;
if (charIsDblQuote) acc.processingKey = !acc.processingKey;
if (charIsDblQuote && !acc.processingKey && charAfter === ':') {
acc.result[acc.processedKey] = acc.position;
acc.processedKey = '';
}
return acc;
},
{
finished: false,
processingKey: false,
processedKey: '',
nesting: 0,
position: targetArrayIndexOf + 1,
result: {}
}
)
return result;
}
const result = getValuesPositionInArray('Statements')(data);
console.log(result)
Ответ 3
Чтобы найти положение чего-то в строке json, если вы хотите создать свой собственный алгоритм, есть несколько вещей, которые нужно учитывать, одна из проблем заключается в том, что несколько строк могут привести к одному и тому же объекту литерала, а также порядок свойств в объекты не гарантируются, то такая же строка может привести к разному порядку в свойствах. Мы знаем, что каждый .
означает {
в строке, но [
может означать [
или {
. Итак, чтобы найти позицию 1
например, мы должны удалить пробелы в исходной строке и выполнить рекурсивные циклы и снова построить json и найти совпадение. Вот только пример, чтобы найти позицию 1:
var json = '{"Version":"XXX","Statements":[{"a":1},{"b":2},{"b":3}]}';
var obj = JSON.parse(json)
var str2 = ""
for(p in obj){
str2 += "{";
str2 += p+":";
if(p == "Statements"){
str2 += ":["
obj[p].forEach(o=>{
for(p2 in o){
if(p2 == "a"){
str2 += '{"a":'
}
}
})
}else{
str2 +='"'+obj[p]+'",'
}
}
console.log(str2)
console.log(str2.length+1)