Created
September 14, 2018 16:53
-
-
Save antonmedv/3aaf9f9648ba213f874b6565d904ccc0 to your computer and use it in GitHub Desktop.
Function for fast extraction of part of JSON
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Fast extract part of json by path. | |
* Example: fastJSON('{...}', ['foo', 'bar']) | |
* | |
* @param {String} input JSON string | |
* @param {Array} path Path for extraction | |
* @returns {String} Extracted JSON string | |
* @throws SyntaxError | |
*/ | |
function fastJSON(input, path) { | |
let lookup = path.shift() // Holds key what we should find next | |
let record = false // Triggers setting of next variables. | |
let start = 0, end = 0 // Holds found offsets in input. | |
const stack = [] // Count brackets, 0 and 1 represents { and [ correspondingly. | |
let level = 0 // Current depth level. | |
let on = 1 // What level we are expecting right now. | |
loop: for (let i = 0, len = input.length; i < len; i++) { | |
const ch = input[i] | |
switch (ch) { | |
case '{': | |
stack.push(0) | |
level++ | |
break | |
case '}': | |
if (stack.pop() !== 0) { | |
throw new SyntaxError(`Unexpected token ${ch} in JSON at position ${i}`) | |
} | |
level-- | |
if (record && level < on) { | |
end = i - 1 | |
break loop | |
} | |
break | |
case '[': | |
stack.push(1) | |
level++ | |
break | |
case ']': | |
if (stack.pop() !== 1) { | |
throw new SyntaxError(`Unexpected token ${ch} in JSON at position ${i}`) | |
} | |
level-- | |
if (record && level < on) { | |
end = i - 1 | |
break loop | |
} | |
break | |
case ':': | |
if (record && level === on) { | |
start = i + 1 | |
} | |
break | |
case ',': | |
if (record && level === on) { | |
end = i - 1 | |
break loop | |
} | |
break | |
case '"': | |
let j = ++i // next char after " | |
// Consume whole string till next " symbol. | |
for (; j < len; j++) { | |
const ch = input[j] | |
if (ch === '"' && input[j - 1] !== '\\') { // Make sure " doesn't escaped. | |
break | |
} | |
} | |
// Check if current key is what we was looking for. | |
if (level === on && input.slice(i, j) === lookup) { | |
if (path.length > 0) { | |
lookup = path.shift() | |
on++ | |
} else { | |
record = true | |
} | |
} | |
i = j // Continue from end of string. | |
break | |
} | |
} | |
if (start !== 0 && start < end) { | |
return input.slice(start, end + 1) // We found it. | |
} else if (level !== 0) { | |
throw new SyntaxError(`Unexpected end of JSON input`) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This function really efficient on very big JSON files.
Around 3-4 times faster what jq.