convert-csv-to-json 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +10 -0
- package/.github/workflows/codeql-analysis.yml +4 -4
- package/.github/workflows/nodejs.yml +3 -3
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/csv-plugin.xml +51 -0
- package/.idea/csvToJson.iml +9 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/README.md +62 -19
- package/SECURITY.md +9 -0
- package/index.js +15 -0
- package/package.json +3 -3
- package/src/csvToJson.js +113 -9
|
@@ -35,11 +35,11 @@ jobs:
|
|
|
35
35
|
|
|
36
36
|
steps:
|
|
37
37
|
- name: Checkout repository
|
|
38
|
-
uses: actions/checkout@
|
|
38
|
+
uses: actions/checkout@v3
|
|
39
39
|
|
|
40
40
|
# Initializes the CodeQL tools for scanning.
|
|
41
41
|
- name: Initialize CodeQL
|
|
42
|
-
uses: github/codeql-action/init@
|
|
42
|
+
uses: github/codeql-action/init@v2
|
|
43
43
|
with:
|
|
44
44
|
languages: ${{ matrix.language }}
|
|
45
45
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
@@ -50,7 +50,7 @@ jobs:
|
|
|
50
50
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
51
51
|
# If this step fails, then you should remove it and run the build manually (see below)
|
|
52
52
|
- name: Autobuild
|
|
53
|
-
uses: github/codeql-action/autobuild@
|
|
53
|
+
uses: github/codeql-action/autobuild@v2
|
|
54
54
|
|
|
55
55
|
# ℹ️ Command-line programs to run using the OS shell.
|
|
56
56
|
# 📚 https://git.io/JvXDl
|
|
@@ -64,4 +64,4 @@ jobs:
|
|
|
64
64
|
# make release
|
|
65
65
|
|
|
66
66
|
- name: Perform CodeQL Analysis
|
|
67
|
-
uses: github/codeql-action/analyze@
|
|
67
|
+
uses: github/codeql-action/analyze@v2
|
|
@@ -9,12 +9,12 @@ jobs:
|
|
|
9
9
|
|
|
10
10
|
strategy:
|
|
11
11
|
matrix:
|
|
12
|
-
node-version: [12.x, 14.x, 16.x]
|
|
12
|
+
node-version: [12.x, 14.x, 16.x, 18.x]
|
|
13
13
|
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
15
|
+
- uses: actions/checkout@v3
|
|
16
16
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
17
|
-
uses: actions/setup-node@
|
|
17
|
+
uses: actions/setup-node@v3
|
|
18
18
|
with:
|
|
19
19
|
node-version: ${{ matrix.node-version }}
|
|
20
20
|
- name: npm install, build, and test
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="CsvFileAttributes">
|
|
4
|
+
<option name="attributeMap">
|
|
5
|
+
<map>
|
|
6
|
+
<entry key="/test/resource/input.csv">
|
|
7
|
+
<value>
|
|
8
|
+
<Attribute>
|
|
9
|
+
<option name="separator" value=";" />
|
|
10
|
+
</Attribute>
|
|
11
|
+
</value>
|
|
12
|
+
</entry>
|
|
13
|
+
<entry key="/test/resource/input_example.csv">
|
|
14
|
+
<value>
|
|
15
|
+
<Attribute>
|
|
16
|
+
<option name="separator" value=";" />
|
|
17
|
+
</Attribute>
|
|
18
|
+
</value>
|
|
19
|
+
</entry>
|
|
20
|
+
<entry key="/test/resource/input_example_sub_array.csv">
|
|
21
|
+
<value>
|
|
22
|
+
<Attribute>
|
|
23
|
+
<option name="separator" value=";" />
|
|
24
|
+
</Attribute>
|
|
25
|
+
</value>
|
|
26
|
+
</entry>
|
|
27
|
+
<entry key="/test/resource/input_quoted_fields.csv">
|
|
28
|
+
<value>
|
|
29
|
+
<Attribute>
|
|
30
|
+
<option name="separator" value="," />
|
|
31
|
+
</Attribute>
|
|
32
|
+
</value>
|
|
33
|
+
</entry>
|
|
34
|
+
<entry key="/test/resource/input_quoted_fields_with_subarray.csv">
|
|
35
|
+
<value>
|
|
36
|
+
<Attribute>
|
|
37
|
+
<option name="separator" value="," />
|
|
38
|
+
</Attribute>
|
|
39
|
+
</value>
|
|
40
|
+
</entry>
|
|
41
|
+
<entry key="/test/resource/input_tilde_delimiter.csv">
|
|
42
|
+
<value>
|
|
43
|
+
<Attribute>
|
|
44
|
+
<option name="separator" value="," />
|
|
45
|
+
</Attribute>
|
|
46
|
+
</value>
|
|
47
|
+
</entry>
|
|
48
|
+
</map>
|
|
49
|
+
</option>
|
|
50
|
+
</component>
|
|
51
|
+
</project>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="JAVA_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
4
|
+
<exclude-output />
|
|
5
|
+
<content url="file://$MODULE_DIR$" />
|
|
6
|
+
<orderEntry type="inheritedJdk" />
|
|
7
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
8
|
+
</component>
|
|
9
|
+
</module>
|
package/.idea/misc.xml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/csvToJson.iml" filepath="$PROJECT_DIR$/.idea/csvToJson.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
package/.idea/vcs.xml
ADDED
package/README.md
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
1
|
# CSVtoJSON
|
|
2
|
-
](https://github.com/iuccio/csvToJson/actions?query=workflow%3ACodeQL)
|
|
5
|
-
[](https://codeclimate.com/github/iuccio/csvToJson)
|
|
2
|
+

|
|
3
|
+

|
|
6
4
|
[](https://snyk.io/test/github/iuccio/csvToJson)
|
|
5
|
+
[](https://codeclimate.com/github/iuccio/csvToJson)
|
|
7
6
|
[](https://npmjs.org/package/convert-csv-to-json)
|
|
7
|
+

|
|
8
8
|
[](https://npmjs.org/package/convert-csv-to-json)
|
|
9
9
|
|
|
10
10
|
**This project is not dependent on others packages or libraries.**
|
|
11
11
|
|
|
12
|
+
|
|
12
13
|
## Table of Contents
|
|
13
|
-
1. [Description](#description)
|
|
14
|
-
1. [Prerequisites](#prerequisites)
|
|
15
|
-
1. [Install npm *convert-csv-to-json package*](#install-npm-convert-csv-to-json-package)
|
|
16
|
-
1. [Usage](#usage)
|
|
17
|
-
* [Generate JSON file](#generate-json-file)
|
|
18
|
-
* [Generate Array of Object in JSON format](#generate-array-of-object-in-json-format)
|
|
19
|
-
* [Generate Object with sub array](#generate-object-with-sub-array)
|
|
20
|
-
* [Define field delimiter](#define-field-delimiter)
|
|
21
|
-
* [Format property value by type](#format-property-value-by-type)
|
|
22
|
-
* [Number](#number)
|
|
23
|
-
* [Boolean](#boolean)
|
|
24
|
-
* [Encoding](#encoding)
|
|
25
|
-
1. [Development](#development)
|
|
26
|
-
1. [License](#license)
|
|
27
|
-
1. [Buy me a Coffee](#buy-me-a-coffee)
|
|
28
14
|
|
|
15
|
+
<!-- toc -->
|
|
16
|
+
|
|
17
|
+
- [Description](#description)
|
|
18
|
+
- [Prerequisites](#prerequisites)
|
|
19
|
+
- [Install npm *convert-csv-to-json package*](#install-npm-convert-csv-to-json-package)
|
|
20
|
+
* [Install](#install)
|
|
21
|
+
* [Usage](#usage)
|
|
22
|
+
+ [Generate JSON file](#generate-json-file)
|
|
23
|
+
+ [Generate Array of Object in JSON format](#generate-array-of-object-in-json-format)
|
|
24
|
+
+ [Generate Object with sub array](#generate-object-with-sub-array)
|
|
25
|
+
+ [Define field delimiter](#define-field-delimiter)
|
|
26
|
+
+ [Support Quoted Fields](#support-quoted-fields)
|
|
27
|
+
+ [Index header](#index-header)
|
|
28
|
+
+ [Empty rows](#empty-rows)
|
|
29
|
+
+ [Format property value by type](#format-property-value-by-type)
|
|
30
|
+
- [Number](#number)
|
|
31
|
+
- [Boolean](#boolean)
|
|
32
|
+
+ [Encoding](#encoding)
|
|
33
|
+
- [Development](#development)
|
|
34
|
+
- [License](#license)
|
|
35
|
+
- [Buy me a Coffee](#buy-me-a-coffee)
|
|
36
|
+
|
|
37
|
+
<!-- tocstop -->
|
|
29
38
|
|
|
30
39
|
## Description
|
|
31
40
|
Converts *csv* files to *JSON* files with Node.js.
|
|
@@ -142,6 +151,40 @@ E.g. if your field delimiter is the comma **,** then:
|
|
|
142
151
|
csvToJson.fieldDelimiter(',').getJsonFromCsv(fileInputName);
|
|
143
152
|
```
|
|
144
153
|
|
|
154
|
+
#### Support Quoted Fields
|
|
155
|
+
To be able to parse correctly fields wrapped in quote, like the **last_name** in the first row in the following example:
|
|
156
|
+
|
|
157
|
+
|first_name| last_name |email|
|
|
158
|
+
|:----------:|:--------------------------:|:---:|
|
|
159
|
+
|Constantin| "Langsdon,Nandson,Gangson" |clangsdon0@hc360.com|
|
|
160
|
+
|
|
161
|
+
you need to activate the support quoted fields feature:
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
csvToJson.supportQuotedField(true).getJsonFromCsv(fileInputName);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The result will be:
|
|
168
|
+
```json
|
|
169
|
+
[
|
|
170
|
+
{
|
|
171
|
+
"firstName": "Constantin",
|
|
172
|
+
"lastName": "Langsdon,Nandson,Gangson",
|
|
173
|
+
"email": "clangsdon0@hc360.com"
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Index header
|
|
179
|
+
If the header is not on the first line you can define the header index like:
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
csvToJson.indexHeader(3).getJsonFromCsv(fileInputName);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Empty rows
|
|
186
|
+
Empty rows are ignored and not parsed.
|
|
187
|
+
|
|
145
188
|
#### Format property value by type
|
|
146
189
|
If you want that a number will be printed as a Number type, and values *true* or *false* is printed as a boolean Type, use:
|
|
147
190
|
```js
|
package/SECURITY.md
CHANGED
|
@@ -14,9 +14,18 @@ A generated Github Depedabot PR is normally quickly merged and a new version is
|
|
|
14
14
|
|
|
15
15
|
| Version | Supported |
|
|
16
16
|
| ------- | ------------------ |
|
|
17
|
+
| 1.4.0 | :white_check_mark: |
|
|
17
18
|
| 1.3.3 | :white_check_mark: |
|
|
18
19
|
| 1.3.2 | :white_check_mark: |
|
|
19
20
|
|
|
21
|
+
### Dev Dependencies
|
|
22
|
+
|
|
23
|
+
| Version | Supported |
|
|
24
|
+
| ------- | ------------------ |
|
|
25
|
+
| 1.4.0 | :white_check_mark: |
|
|
26
|
+
| 1.3.3 | :warning: moderate dev dependencies vulnerability|
|
|
27
|
+
| 1.3.2 | :warning: moderate dev dependencies vulnerability|
|
|
28
|
+
|
|
20
29
|
## Reporting a Vulnerability
|
|
21
30
|
|
|
22
31
|
To report a Security Vulnerability please add an [Vulnerabilty Issue](https://github.com/iuccio/csvToJson/labels/vulnerabilty).
|
package/index.js
CHANGED
|
@@ -20,6 +20,13 @@ exports.formatValueByType = function (active = true) {
|
|
|
20
20
|
return this;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
exports.supportQuotedField = function (active = false) {
|
|
27
|
+
csvToJson.supportQuotedField(active);
|
|
28
|
+
return this;
|
|
29
|
+
};
|
|
23
30
|
/**
|
|
24
31
|
* Defines the field delimiter which will be used to split the fields
|
|
25
32
|
*/
|
|
@@ -28,6 +35,14 @@ exports.fieldDelimiter = function (delimiter) {
|
|
|
28
35
|
return this;
|
|
29
36
|
};
|
|
30
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Defines the index where the header is defined
|
|
40
|
+
*/
|
|
41
|
+
exports.indexHeader = function (index) {
|
|
42
|
+
csvToJson.indexHeader(index);
|
|
43
|
+
return this;
|
|
44
|
+
};
|
|
45
|
+
|
|
31
46
|
/**
|
|
32
47
|
* Defines how to match and parse a sub array
|
|
33
48
|
*/
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "convert-csv-to-json",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Convert CSV to JSON",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "./node_modules/.bin/mocha --reporter spec",
|
|
7
|
+
"test": "./node_modules/.bin/mocha --parallel --reporter spec",
|
|
8
8
|
"test-watch": "./node_modules/.bin/mocha -w"
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
@@ -37,6 +37,6 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"chai": "^4.3.7",
|
|
39
39
|
"chai-fs": "^2.0.0",
|
|
40
|
-
"mocha": "^10.
|
|
40
|
+
"mocha": "^10.2.0"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/src/csvToJson.js
CHANGED
|
@@ -14,11 +14,25 @@ class CsvToJson {
|
|
|
14
14
|
return this;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
this.
|
|
17
|
+
supportQuotedField(active) {
|
|
18
|
+
this.isSupportQuotedField = active;
|
|
19
19
|
return this;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
fieldDelimiter(delimiter) {
|
|
23
|
+
this.delimiter = delimiter;
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
indexHeader(indexHeader) {
|
|
28
|
+
if(isNaN(indexHeader)){
|
|
29
|
+
throw new Error('The index Header must be a Number!');
|
|
30
|
+
}
|
|
31
|
+
this.indexHeader = indexHeader;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
22
36
|
parseSubArray(delimiter = '*',separator = ',') {
|
|
23
37
|
this.parseSubArrayDelimiter = delimiter;
|
|
24
38
|
this.parseSubArraySeparator = separator;
|
|
@@ -51,17 +65,30 @@ class CsvToJson {
|
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
csvToJson(parsedCsv) {
|
|
68
|
+
this.validateInputConfig();
|
|
54
69
|
let lines = parsedCsv.split(newLine);
|
|
55
70
|
let fieldDelimiter = this.getFieldDelimiter();
|
|
56
|
-
let
|
|
71
|
+
let index = this.getIndexHeader();
|
|
72
|
+
let headers = lines[index].split(fieldDelimiter);
|
|
57
73
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (stringUtils.hasContent(currentLine)) {
|
|
62
|
-
jsonResult.push(this.buildJsonResult(headers, currentLine));
|
|
63
|
-
}
|
|
74
|
+
while(!stringUtils.hasContent(headers) && index <= lines.length){
|
|
75
|
+
index = index + 1;
|
|
76
|
+
headers = lines[index].split(fieldDelimiter);
|
|
64
77
|
}
|
|
78
|
+
|
|
79
|
+
let jsonResult = [];
|
|
80
|
+
for (let i = (index + 1); i < lines.length; i++) {
|
|
81
|
+
let currentLine;
|
|
82
|
+
if(this.isSupportQuotedField){
|
|
83
|
+
currentLine = this.split(lines[i]);
|
|
84
|
+
}
|
|
85
|
+
else{
|
|
86
|
+
currentLine = lines[i].split(fieldDelimiter);
|
|
87
|
+
}
|
|
88
|
+
if (stringUtils.hasContent(currentLine)) {
|
|
89
|
+
jsonResult.push(this.buildJsonResult(headers, currentLine));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
65
92
|
return jsonResult;
|
|
66
93
|
}
|
|
67
94
|
|
|
@@ -72,6 +99,13 @@ class CsvToJson {
|
|
|
72
99
|
return defaultFieldDelimiter;
|
|
73
100
|
}
|
|
74
101
|
|
|
102
|
+
getIndexHeader(){
|
|
103
|
+
if(this.indexHeader !== null && !isNaN(this.indexHeader)){
|
|
104
|
+
return this.indexHeader;
|
|
105
|
+
}
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
75
109
|
buildJsonResult(headers, currentLine) {
|
|
76
110
|
let jsonObject = {};
|
|
77
111
|
for (let j = 0; j < headers.length; j++) {
|
|
@@ -115,6 +149,76 @@ class CsvToJson {
|
|
|
115
149
|
return false;
|
|
116
150
|
}
|
|
117
151
|
|
|
152
|
+
validateInputConfig(){
|
|
153
|
+
if(this.isSupportQuotedField) {
|
|
154
|
+
if(this.getFieldDelimiter() === '"'){
|
|
155
|
+
throw new Error('When SupportQuotedFields is enabled you cannot defined the field delimiter as quote -> ["]');
|
|
156
|
+
}
|
|
157
|
+
if(this.parseSubArraySeparator === '"'){
|
|
158
|
+
throw new Error('When SupportQuotedFields is enabled you cannot defined the field parseSubArraySeparator as quote -> ["]');
|
|
159
|
+
}
|
|
160
|
+
if(this.parseSubArrayDelimiter === '"'){
|
|
161
|
+
throw new Error('When SupportQuotedFields is enabled you cannot defined the field parseSubArrayDelimiter as quote -> ["]');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
hasQuotes(line) {
|
|
167
|
+
return line.includes('"');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
split(line) {
|
|
171
|
+
if(line.length == 0){
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
let delim = this.getFieldDelimiter();
|
|
175
|
+
let subSplits = [''];
|
|
176
|
+
if (this.hasQuotes(line)) {
|
|
177
|
+
let chars = line.split('');
|
|
178
|
+
|
|
179
|
+
let subIndex = 0;
|
|
180
|
+
let startQuote = false;
|
|
181
|
+
let isDouble = false;
|
|
182
|
+
chars.forEach((c, i, arr) => {
|
|
183
|
+
if (isDouble) { //when run into double just pop it into current and move on
|
|
184
|
+
subSplits[subIndex] += c;
|
|
185
|
+
isDouble = false;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (c != '"' && c != delim ) {
|
|
190
|
+
subSplits[subIndex] += c;
|
|
191
|
+
} else if(c == delim && startQuote){
|
|
192
|
+
subSplits[subIndex] += c;
|
|
193
|
+
} else if( c == delim ){
|
|
194
|
+
subIndex++
|
|
195
|
+
subSplits[subIndex] = '';
|
|
196
|
+
return;
|
|
197
|
+
} else {
|
|
198
|
+
if (arr[i + 1] === '"') {
|
|
199
|
+
//Double quote
|
|
200
|
+
isDouble = true;
|
|
201
|
+
//subSplits[subIndex] += c; //Skip because this is escaped quote
|
|
202
|
+
} else {
|
|
203
|
+
if (!startQuote) {
|
|
204
|
+
startQuote = true;
|
|
205
|
+
//subSplits[subIndex] += c; //Skip because we don't want quotes wrapping value
|
|
206
|
+
} else {
|
|
207
|
+
//end
|
|
208
|
+
startQuote = false;
|
|
209
|
+
//subSplits[subIndex] += c; //Skip because we don't want quotes wrapping value
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
if(startQuote){
|
|
215
|
+
throw new Error('Row contains mismatched quotes!');
|
|
216
|
+
}
|
|
217
|
+
return subSplits;
|
|
218
|
+
} else {
|
|
219
|
+
return line.split(delim);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
118
222
|
}
|
|
119
223
|
|
|
120
224
|
module.exports = new CsvToJson();
|