Skip to content

Commit b077c86

Browse files
#159 added JSON Line data files support
for .jsonl & .ndjson files + docs & recommended extensions update for those json data file types
1 parent 1b43c82 commit b077c86

File tree

8 files changed

+127
-10
lines changed

8 files changed

+127
-10
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ Use Data Preview 🈸 to:
124124

125125
```js
126126
{
127-
"when": "resourceFilename =~ /.*\\.(json|json5|hjson|arrow|arr|avro|env|config|properties|ini|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsx|xlsm|xml|html)/",
127+
"when": "resourceFilename =~ /.*\\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|env|config|properties|ini|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsx|xlsm|xml|html)/",
128128
"command": "data.preview",
129129
"group": "navigation"
130130
}
@@ -139,7 +139,7 @@ for more info.
139139

140140
| Data File Extension(s) | File Type | Data Parsing Library/Method Used | Data Format Specification |
141141
| --- | --- | --- | --- |
142-
| `.json` `.config` | text | [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) | https://json.org/ |
142+
| `.json` `.config` `.jsonl` `.ndjson` | text | [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) | https://json.org/ |
143143
| `.json5` | text | [json5](https://github.com/json5/json5)/[`JSON5.parse()`](https://github.com/json5/json5#json5parse) | https://json5.org/ |
144144
| `.hjson` | text | [hjson-js](https://github.com/hjson/hjson-js)/[`Hjson.parse()`](https://github.com/hjson/hjson-js#hjsonparsetext-options) | https://hjson.org/ |
145145
| `.arrow` `.arr` | binary | [apache-arrow](https://github.com/apache/arrow/tree/master/js)/[`Table.from()`](https://github.com/apache/arrow/tree/master/js#get-a-table-from-an-arrow-file-on-disk-in-ipc-format) | https://arrow.apache.org/ |
@@ -210,6 +210,7 @@ Other extensions Data Preview 🈸 replaces, enhances or supplements for working
210210
| [Ini for VSCode](https://marketplace.visualstudio.com/items?itemName=DavidWang.ini-for-vscode) | Provides outline view and section folding for INI files |
211211
| [Hjson](https://marketplace.visualstudio.com/items?itemName=laktak.hjson) | Hjson language syntax support |
212212
| [JSON5 syntax](https://marketplace.visualstudio.com/items?itemName=mrmlnc.vscode-json5) | Adds syntax highlighting of JSON5 files |
213+
| [NDJSON Colorizer](https://marketplace.visualstudio.com/items?itemName=buster.ndjson-colorizer) | Colorizes NDJSON (Newline Delimited JSON) files |
213214
| [YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) | YAML Language Support by Red Hat, with built-in Kubernetes and Kedge syntax support |
214215
| [docs-yaml](https://marketplace.visualstudio.com/items?itemName=docsmsft.docs-yaml) | YAML schema validation and auto-completion for docs.microsoft.com authoring |
215216
| [YAML to JSON](https://marketplace.visualstudio.com/items?itemName=ahebrank.yaml2json) | Convert YAML from clipboard or current document/selection to JSON and vice versa |

data/json/poker.ndjson

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
2+
{"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]}
3+
{"name": "May", "wins": []}
4+
{"name": "Deloise", "wins": [["three of a kind", "5♣"]]}

data/json/scores.jsonl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
["Name", "Session", "Score", "Completed"]
2+
["Gilbert", "2013", 24, true]
3+
["Alexa", "2013", 29, true]
4+
["May", "2012B", 14, false]
5+
["Deloise", "2012A", 19, true]

package.json

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@
3838
"data import",
3939
"markdown table",
4040
"json array",
41+
"json line",
42+
"jsonl",
4143
"json5",
4244
"hjson",
45+
"ndjson",
4346
"arrow",
4447
"avro",
4548
"parquet",
@@ -79,6 +82,7 @@
7982
"onWebviewPanel:data.preview",
8083
"onLanguage:json",
8184
"onLanguage:json5",
85+
"onLanguage:jsonl",
8286
"onLanguage:hjson",
8387
"onLanguage:arrow",
8488
"onLanguage:avro",
@@ -114,6 +118,16 @@
114118
"JSON5"
115119
]
116120
},
121+
{
122+
"id": "jsonl",
123+
"extensions": [
124+
".jsonl",
125+
".ndjson"
126+
],
127+
"aliases": [
128+
"JSON Line"
129+
]
130+
},
117131
{
118132
"id": "hjson",
119133
"extensions": [
@@ -275,36 +289,36 @@
275289
"explorer/context": [
276290
{
277291
"command": "data.preview",
278-
"when": "resourceFilename =~ /.*\\.(json|json5|hjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
292+
"when": "resourceFilename =~ /.*\\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
279293
"group": "navigation"
280294
},
281295
{
282296
"command": "data.preview.on.side",
283-
"when": "resourceFilename =~ /.*\\.(json|json5|hjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
297+
"when": "resourceFilename =~ /.*\\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
284298
"group": "navigation"
285299
}
286300
],
287301
"editor/title": [
288302
{
289303
"command": "data.preview",
290-
"when": "resourceFilename =~ /.*\\.(json|json5|hjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
304+
"when": "resourceFilename =~ /.*\\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
291305
"group": "navigation"
292306
},
293307
{
294308
"command": "data.preview.on.side",
295-
"when": "resourceFilename =~ /.*\\.(json|json5|hjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
309+
"when": "resourceFilename =~ /.*\\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
296310
"group": "navigation"
297311
}
298312
],
299313
"editor/title/context": [
300314
{
301315
"command": "data.preview",
302-
"when": "resourceFilename =~ /.*\\.(json|json5|hjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
316+
"when": "resourceFilename =~ /.*\\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
303317
"group": "navigation"
304318
},
305319
{
306320
"command": "data.preview.on.side",
307-
"when": "resourceFilename =~ /.*\\.(json|json5|hjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
321+
"when": "resourceFilename =~ /.*\\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsm|xlsx|xml|html)/",
308322
"group": "navigation"
309323
}
310324
]

src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { LogLevel } from "./logger";
33
// log level setting for prod. vs. dev run of this ext.
44
export const logLevel: LogLevel = LogLevel.Info; // change to .Debug for ext. dev debug
55

6-
export const supportedDataFiles: RegExp = /.*\.(json|json5|hjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsx|xlsm|xml|html)/;
6+
export const supportedDataFiles: RegExp = /.*\.(json|jsonl|json5|hjson|ndjson|arrow|arr|avro|parquet|parq|config|env|properties|ini|yaml|yml|md|csv|tsv|txt|tab|dif|ods|xls|xlsb|xlsx|xlsm|xml|html)/;
77

88
export const supportedBinaryDataFiles: RegExp = /.*\.(arrow|arr|avro|parquet|parq|dif|ods|xls|xlsb|xlsx|xlsm)/;
99

1010
export const supportedFilesFilters: any = {
11-
'JSON': ['json', 'json5', 'hjson'],
11+
'JSON': ['json', 'jsonl', 'json5', 'hjson', 'ndjson'],
1212
'CSV/TSV': ['csv', 'tsv', 'tab', 'txt'],
1313
'Excel': ['dif', 'ods', 'xls', 'xlsb', 'xlsx', 'xlsm', 'xml', 'html'],
1414
'Arrow': ['arrow'],

src/data.manager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {ExcelDataProvider} from './data.providers/excel.data.provider';
1010
import {HjsonDataProvider} from './data.providers/hjson.data.provider';
1111
import {JsonDataProvider} from './data.providers/json.data.provider';
1212
import {Json5DataProvider} from './data.providers/json5.data.provider';
13+
import {JsonLineDataProvider} from './data.providers/json.line.data.provider';
1314
import {MarkdownDataProvider} from './data.providers/markdown.data.provider';
1415
import {TextDataProvider} from './data.providers/text.data.provider';
1516
import {PropertiesDataProvider} from './data.providers/properties.data.provider';
@@ -140,6 +141,7 @@ export class DataManager implements IDataManager {
140141
this.addDataProvider(dataProviders, new HjsonDataProvider());
141142
this.addDataProvider(dataProviders, new JsonDataProvider());
142143
this.addDataProvider(dataProviders, new Json5DataProvider());
144+
this.addDataProvider(dataProviders, new JsonLineDataProvider());
143145
this.addDataProvider(dataProviders, new MarkdownDataProvider());
144146
this.addDataProvider(dataProviders, new TextDataProvider());
145147
this.addDataProvider(dataProviders, new PropertiesDataProvider());
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {window} from 'vscode';
2+
import * as fs from 'fs';
3+
import * as config from '../config';
4+
import * as fileUtils from '../utils/file.utils';
5+
import * as jsonUtils from '../utils/json.utils';
6+
import {Logger, LogLevel} from '../logger';
7+
import {IDataProvider} from '../data.manager';
8+
9+
/**
10+
* JSON Line data provider.
11+
* @see http://jsonlines.org/
12+
*/
13+
export class JsonLineDataProvider implements IDataProvider {
14+
15+
// TODO: add mime types later for http data loading
16+
public supportedDataFileTypes: Array<string> = ['.jsonl', '.ndjson'];
17+
private logger: Logger = new Logger('json.line.data.provider:', config.logLevel);
18+
19+
/**
20+
* Creates new JSON Line data provider for .jsonl and .ndjson data files.
21+
*/
22+
constructor() {
23+
this.logger.debug('created for:', this.supportedDataFileTypes);
24+
}
25+
26+
/**
27+
* Gets local or remote data.
28+
* @param dataUrl Local data file path or remote data url.
29+
* @param parseOptions Data parse options.
30+
* @param loadData Load data callback.
31+
*/
32+
public async getData(dataUrl: string, parseOptions: any, loadData: Function): Promise<void> {
33+
let data: any = [];
34+
let lineIndex = 1;
35+
try {
36+
let content: string = String(await fileUtils.readDataFile(dataUrl, 'utf8'));
37+
const jsonLines: Array<string> = content.split('\n');
38+
jsonLines.forEach(jsonLine => {
39+
const trimmedJsonLine: string = jsonLine.trim();
40+
if (trimmedJsonLine.length > 0) {
41+
data.push(JSON.parse(trimmedJsonLine));
42+
}
43+
lineIndex++;
44+
});
45+
}
46+
catch (error) {
47+
this.logger.logMessage(LogLevel.Error, `getData(): Error parsing '${dataUrl}' \
48+
\n\t line #: ${lineIndex} Error:`, error.message);
49+
window.showErrorMessage(`Unable to parse data file: '${dataUrl}'. \
50+
\n\t Line #: ${lineIndex} Error: ${error.message}`);
51+
}
52+
loadData(jsonUtils.convertJsonData(data));
53+
}
54+
55+
/**
56+
* Gets data table names for data sources with multiple data sets.
57+
* @param dataUrl Local data file path or remote data url.
58+
*/
59+
public getDataTableNames(dataUrl: string): Array<string> {
60+
return []; // none for json data files
61+
}
62+
63+
/**
64+
* Gets data schema in json format for file types that provide it.
65+
* @param dataUrl Local data file path or remote data url.
66+
*/
67+
public getDataSchema(dataUrl: string): any {
68+
// TODO: auto-gen json schema ???
69+
return null; // none for json data files
70+
}
71+
72+
/**
73+
* Saves JSON data.
74+
* @param filePath Local data file path.
75+
* @param fileData Raw data to save.
76+
* @param tableName Table name for data files with multiple tables support.
77+
* @param showData Show saved data callback.
78+
*/
79+
public saveData(filePath: string, fileData: any, tableName: string, showData?: Function): void {
80+
fileData = JSON.stringify(fileData, null, 2);
81+
if ( fileData.length > 0) {
82+
// TODO: change this to async later
83+
fs.writeFile(filePath, fileData, (error) => showData(error));
84+
}
85+
}
86+
87+
}

templates/data.view.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,8 @@
536536
case '.yml':
537537
case '.json5':
538538
case '.hjson':
539+
case '.jsonl':
540+
case '.ndjson':
539541
// pass through loaded data json
540542
tableData = data;
541543
logMessage(`getData()\n\n records count: ${tableData.length.toLocaleString()}`);
@@ -587,8 +589,10 @@
587589
viewer.view.to_csv().then(csv => postData(csv, dataFileType));
588590
break;
589591
case '.json':
592+
case '.jsonl':
590593
case '.json5':
591594
case '.hjson':
595+
case '.ndjson':
592596
case '.html':
593597
case '.ods':
594598
case '.xml':

0 commit comments

Comments
 (0)