From 050ba817608c7c80777d83ab851296836b73fa3e Mon Sep 17 00:00:00 2001 From: Thomas Nordquist Date: Tue, 28 May 2019 17:49:41 +0200 Subject: [PATCH] Prepare json literal inpsection --- backend/src/JsonAstParser.ts | 77 ++++++++++++++++++++++++++ backend/src/spec/JsonAstParser.spec.ts | 48 ++++++++++++++++ package.json | 1 + yarn.lock | 15 ++++- 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 backend/src/JsonAstParser.ts create mode 100644 backend/src/spec/JsonAstParser.spec.ts diff --git a/backend/src/JsonAstParser.ts b/backend/src/JsonAstParser.ts new file mode 100644 index 0000000..b6bb6c0 --- /dev/null +++ b/backend/src/JsonAstParser.ts @@ -0,0 +1,77 @@ +const parse = require('json-to-ast') + +interface JsonPropertyLocation { + path: string + line: number + column: number +} + +type JsonAst = JsonAstLiteral | JsonAstObject | JsonAstArray + +interface JsonAstLocation { + start: { + line: number + column: number + offset: number + } + end: { + line: number + column: number + offset: number + } + source: null | string +} + +interface JsonAstIdentifier { + type: 'Identifier' + value: string + raw: string + loc: JsonAstLocation +} + +interface JsonAstObjectProperty { + type: 'Property' + key: JsonAstIdentifier + value: JsonAst + loc: JsonAstLocation +} + +interface JsonAstLiteral { + type: 'Literal' + value: string | number | boolean | any + raw: string + loc: JsonAstLocation +} + +interface JsonAstObject { + type: 'Object' + children: Array + loc: JsonAstLocation +} + +interface JsonAstArray { + type: 'Array' + children: Array + loc: JsonAstLocation +} + +function jsonToPropertyPaths(ast: JsonAst, previousPath: Array = []): Array { + let children: Array> = [] + if (ast.type === 'Literal') { + return [{ + path: previousPath.join('.'), + line: ast.loc.start.line, + column: ast.loc.start.column, + }] + } else if (ast.type === 'Array') { + children = ast.children.map((value, idx) => jsonToPropertyPaths(value, previousPath.slice().concat([String(idx)]))) + } else if (ast.type === 'Object') { + children = ast.children.map(property => jsonToPropertyPaths(property.value, previousPath.slice().concat([property.key.value]))) + } + + return children.reduce((a, b) => a.concat(b), []) +} + +export function parseJson(formattedJson: string): Array { + return jsonToPropertyPaths((parse(formattedJson) as JsonAst), []) +} diff --git a/backend/src/spec/JsonAstParser.spec.ts b/backend/src/spec/JsonAstParser.spec.ts new file mode 100644 index 0000000..37d43e8 --- /dev/null +++ b/backend/src/spec/JsonAstParser.spec.ts @@ -0,0 +1,48 @@ +import 'mocha' + +import { expect } from 'chai' +import { parseJson } from '../JsonAstParser' +const dotProp = require('dot-prop') + +describe('access JSON values via dot property paths', () => { + it('object with literal', () => { + let data = { + foo: 4, + } + + let result = parseJson(JSON.stringify(data, undefined, 2)) + expect(result[0].path).to.eq('foo') + expect(result[0].line).to.eq(2) + }) + + it('nested object', () => { + let data = { + foo: { + bar: 4 + }, + } + + let result = parseJson(JSON.stringify(data, undefined, 2)) + expect(result[0].path).to.eq('foo.bar') + expect(result[0].line).to.eq(3) + expect(dotProp.get(data, result[0].path)).to.eq(4) + + }) + + it('array path', () => { + let data = { + foo: [ + 1, + 2, + 3, + ], + } + + let result = parseJson(JSON.stringify(data, undefined, 2)) + expect(result.length).to.eq(3) + + expect(result[2].path).to.eq('foo.2') + expect(result[2].line).to.eq(5) + expect(dotProp.get(data, result[2].path)).to.eq(3) + }) +}) diff --git a/package.json b/package.json index 170a32f..0d25c9e 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist", "electron-updater": "^4.0.6", "js-base64": "^2.5.1", + "json-to-ast": "^2.1.0", "lowdb": "^1.0.0", "mqtt": "^2.18.8", "sha1": "^1.1.1", diff --git a/yarn.lock b/yarn.lock index ef9e475..4b05209 100644 --- a/yarn.lock +++ b/yarn.lock @@ -753,6 +753,11 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +code-error-fragment@0.0.230: + version "0.0.230" + resolved "https://registry.yarnpkg.com/code-error-fragment/-/code-error-fragment-0.0.230.tgz#d736d75c832445342eca1d1fedbf17d9618b14d7" + integrity sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw== + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -1721,7 +1726,7 @@ graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== -grapheme-splitter@^1.0.2: +grapheme-splitter@^1.0.2, grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== @@ -2200,6 +2205,14 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json-to-ast@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json-to-ast/-/json-to-ast-2.1.0.tgz#041a9fcd03c0845036acb670d29f425cea4faaf9" + integrity sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ== + dependencies: + code-error-fragment "0.0.230" + grapheme-splitter "^1.0.4" + json5@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850"