UNPKG

customized-json

Version:

A JSON parser that allows users to overwrite default parsing behavior by customized rules defined in a user-defined function that provides more useful parameters than the reviver function of JSON.parse().

167 lines (151 loc) 5.77 kB
const parse = (jsonString, reviver) => { if (reviver == null || reviver.length <= 2) { return JSON.parse(jsonString, reviver) } else { const standardJSON = JSON.parse(jsonString) return parseValue(jsonString, 0, [], standardJSON, reviver).modifiedJSON } } const parseValue = (jsonString, currentCharIndex, parentKeys, parsedJSON, reviver) => { let valueObject let i = currentCharIndex for (i; i < jsonString.length; i++) { const currentChar = jsonString.charAt(i) if (currentChar === '"') { valueObject = parseString(jsonString, i) break } else if (isNumericValue(currentChar)) { valueObject = parseNumber(jsonString, i) break } else if (currentChar === 't' || currentChar === 'f') { valueObject = parseBoolean(jsonString, i) break } else if (currentChar === 'n') { valueObject = parseNull(jsonString, i) break } else if (currentChar === '{') { valueObject = parseObject(jsonString, i, parentKeys, parsedJSON, reviver) break } else if (currentChar === '[') { valueObject = parseArray(jsonString, i, parentKeys, parsedJSON, reviver) break } } const modifiedJSON = prepareReviver(parentKeys, valueObject.value, valueObject.modifiedJSON === undefined ? parsedJSON : valueObject.modifiedJSON, reviver) return { value: valueObject.value, lastValueIndex: valueObject.lastValueIndex, modifiedJSON: modifiedJSON } } const parseObject = (jsonString, currentCharIndex, parentKeys, parsedJSON, reviver) => { let keyFound = false const object = {} let modifiedJSON for (let i = currentCharIndex; i < jsonString.length; i++) { const char = jsonString.charAt(i) if (char === '}') { return { value: object, lastValueIndex: i, modifiedJSON } } else if (keyFound) { const currentKey = parentKeys[parentKeys.length - 1] const valueObject = parseValue(jsonString, i, parentKeys, parsedJSON, reviver) object[currentKey] = valueObject.value i = valueObject.lastValueIndex modifiedJSON = valueObject.modifiedJSON parentKeys.pop() keyFound = false } else { if (char === '"') { const { value, lastValueIndex } = parseString(jsonString, i) parentKeys.push(value) keyFound = true i = lastValueIndex } } } } const isNumericValue = (char) => /\d/.test(char) || char === '-' || char.toLowerCase() === 'e' const parseString = (jsonString, currentCharIndex) => { let value = '' let firstQuotationMarkFound = false let isEscaped = false let escapeFound = false for (let i = currentCharIndex; i < jsonString.length; i++) { const currentChar = jsonString.charAt(i) if (currentChar === '\\') { if (!isEscaped) { isEscaped = true escapeFound = true } } else { if (currentChar === '"') { if (!isEscaped) { if (firstQuotationMarkFound) { if (escapeFound) { value = unescapeJSONString(value) } return { value: value, lastValueIndex: i } } else { firstQuotationMarkFound = true continue } } } isEscaped = false } value += currentChar } } const unescapeJSONString = (value) => JSON.parse(`"${value}"`) const parseNumber = (jsonString, currentCharIndex) => { let currentChar let value = '' let i = currentCharIndex let lastValueIndex = i while (i < jsonString.length && isNumericValue(currentChar = jsonString.charAt(i))) { value += currentChar lastValueIndex = i++ } return { value: value, lastValueIndex: lastValueIndex } } const parseBoolean = (jsonString, currentCharIndex) => { const lastBooleanValueIndex = jsonString.charAt(currentCharIndex) === 't' ? currentCharIndex + 3 : currentCharIndex + 4 const value = jsonString.substring(currentCharIndex, lastBooleanValueIndex + 1) return { value: value, lastValueIndex: lastBooleanValueIndex } } const parseNull = (jsonString, currentCharIndex) => { return { value: null, lastValueIndex: currentCharIndex + 3 } } const parseArray = (jsonString, currentCharIndex, parentKeys, parsedJSON, reviver) => { const array = [] let modifiedJSON for (let i = currentCharIndex + 1; i < jsonString.length; i++) { const currentChar = jsonString.charAt(i) if (currentChar === ']') { return { value: array, lastValueIndex: i, modifiedJSON: modifiedJSON } } else if (isWhitespace(currentChar)) { continue } parentKeys.push(array.length) const valueObject = parseValue(jsonString, i, parentKeys, parsedJSON, reviver) parentKeys.pop() array.push(valueObject.value) i = valueObject.lastValueIndex modifiedJSON = valueObject.modifiedJSON } } const isWhitespace = (character) => { return character.trim() === '' } const prepareReviver = (parentKeys, stringValue, parsedJSON, reviver) => { const originalValue = findOriginalValue(parsedJSON, parentKeys) return setKeyValue(parsedJSON, parentKeys, reviver(parentKeys[parentKeys.length - 1], originalValue, stringValue, parsedJSON, parentKeys)) } const findOriginalValue = (parsedJSON, parentKeys) => parentKeys.reduce((accumulator, currentValue) => accumulator[currentValue], parsedJSON) const setKeyValue = (parsedJSON, parentKeys, newValue) => { if (parentKeys.length === 0) { return newValue } else { findOriginalValue(parsedJSON, parentKeys.slice(0, parentKeys.length - 1))[parentKeys[parentKeys.length - 1]] = newValue return parsedJSON } } module.exports.parse = parse