I have a JSON string similar to this one:
{
"Version": "XXX",
"Statements": [
{...},
{...},
{...}
]
}
How can I find out which object inside Statements property is defined at character XX
of the JSON string? (considering that those objects can have arbitrarily deep nesting).
For example, if I have a string
{"Version":"XXX","Statements":[{"a":1},{"b":2},{"b":3}]}
--------------------------------------------------------
123456789 123456789 123456789 123456789 123456789 123456
then character at position 36
would correspond to the first statement object, while character at position 52
would correspond to the third statement object.
Here is some dirty solution that doesn't require external libs:
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)
But this snippet will break if target objects would contain string values.
EDIT
And below is updated snippet with string values fix and with parsed values as well:
const data = '{"Version":"XXX","Statements":[{"aa":"some"},{"b":"ano:,{ther}"},{"bb":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 charsAfterValue = ['}', ','];
const charsBeforeKey = ['{', ','];
const { result } = arrayStringWithRest.split('').reduce(
(acc, char, idx, array) => {
if (acc.finished) return acc;
if (!acc.processingKey && !acc.processingValue && char === '[') acc.nesting += 1;
if (!acc.processingKey && !acc.processingValue && char === ']') acc.nesting -= 1;
const shouldFinish = acc.nesting === 0;
const charIsDblQuote = char === '"';
const charBefore = array[idx - 1];
const charAfter = array[idx + 1];
const keyProcessingStarted = (
charIsDblQuote &&
!acc.processingKey &&
!acc.processingValue &&
charsBeforeKey.includes(charBefore)
);
const keyProcessingFinished = (
charAfter === ':' &&
charIsDblQuote &&
acc.processingKey
);
const valueProcessingStarted = (
char === ':' &&
!acc.processingKey &&
!acc.processingValue
);
const valueProcessingFinished = (
(acc.lastProcessedValueType === String
? charIsDblQuote
: true
) &&
acc.processingValue &&
charsAfterValue.includes(charAfter)
);
acc.position += 1;
acc.finished = shouldFinish;
if (acc.processingKey && !charIsDblQuote) acc.processedKey += char;
if (acc.processingValue && !charIsDblQuote) acc.processedValue += char;
if (keyProcessingStarted) {
acc.processingKey = true;
} else if (keyProcessingFinished) {
acc.processingKey = false;
acc.result[acc.processedKey] = { position: acc.position };
acc.lastProcessedKey = acc.processedKey;
acc.processedKey = '';
}
if (valueProcessingStarted) {
acc.processingValue = true;
acc.lastProcessedValueType = charAfter === '"' ? String : Number;
} else if (valueProcessingFinished) {
acc.processingValue = false;
acc.result[acc.lastProcessedKey].value = (
acc.lastProcessedValueType(acc.processedValue)
);
acc.processedValue = '';
acc.lastProcessedKey = '';
acc.lastProcessedValueType = (v) => v;
}
return acc;
},
{
finished: false,
processingKey: false,
processingValue: false,
processedKey: '',
processedValue: '',
lastProcessedKey: '',
lastProcessedValueType: (v) => v,
nesting: 0,
position: targetArrayIndexOf + 1,
result: {}
}
)
return result;
}
const result = getValuesPositionInArray('Statements')(data);
console.log(result)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With