convert-csv-to-json 3.12.0 → 3.13.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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - New `csvStringToJsonStringified()` method that converts CSV content directly to a validated JSON string without requiring file I/O
12
+ - Improved test coverage for string-based CSV parsing
13
+
14
+ ### Changed
15
+ - No breaking changes - all existing APIs remain unchanged
package/README.md CHANGED
@@ -41,6 +41,7 @@ show your :heart: and support.
41
41
  - [Number](#number)
42
42
  - [Boolean](#boolean)
43
43
  + [Encoding](#encoding)
44
+ + [Working with CSV strings directly](#working-with-csv-strings-directly)
44
45
  * [Chaining Pattern](#chaining-pattern)
45
46
  - [Development](#development)
46
47
  - [CI CD github action](#ci-cd-github-action)
@@ -220,13 +221,49 @@ If the header is not on the first line you can define the header index like:
220
221
  Empty rows are ignored and not parsed.
221
222
 
222
223
  #### Format property value by type
223
- 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:
224
+ The `formatValueByType()` function intelligently converts string values to their appropriate types while preserving data integrity. To enable automatic type conversion:
225
+
224
226
  ```js
225
- csvToJson.formatValueByType()
226
- .getJsonFromCsv(fileInputName);
227
+ csvToJson.formatValueByType()
228
+ .getJsonFromCsv(fileInputName);
227
229
  ```
228
- For example:
229
230
 
231
+ This conversion follows these rules:
232
+
233
+ ##### Numbers
234
+ - Regular integers and decimals are converted to Number type
235
+ - Numbers with leading zeros are preserved as strings (e.g., "0012" stays "0012")
236
+ - Large integers outside JavaScript's safe range are preserved as strings
237
+ - Valid decimal numbers are converted to Number type
238
+
239
+ For example:
240
+ ```json
241
+ {
242
+ "normalInteger": 42, // Converted to number
243
+ "decimal": 3.14, // Converted to number
244
+ "leadingZeros": "0012345", // Kept as string to preserve leading zeros
245
+ "largeNumber": "9007199254740992" // Kept as string to preserve precision
246
+ }
247
+ ```
248
+
249
+ ##### Boolean
250
+ Case-insensitive "true" or "false" strings are converted to boolean values:
251
+ ```json
252
+ {
253
+ "registered": true, // From "true" or "TRUE" or "True"
254
+ "active": false // From "false" or "FALSE" or "False"
255
+ }
256
+ ```
257
+
258
+ ##### Complete Example
259
+ Input CSV:
260
+ ```csv
261
+ first_name;last_name;email;gender;age;id;zip;registered
262
+ Constantin;Langsdon;clangsdon0@hc360.com;Male;96;00123;123;true
263
+ Norah;Raison;nraison1@wired.com;Female;32;987;00456;FALSE
264
+ ```
265
+
266
+ Output JSON:
230
267
  ```json
231
268
  [
232
269
  {
@@ -235,8 +272,9 @@ For example:
235
272
  "email": "clangsdon0@hc360.com",
236
273
  "gender": "Male",
237
274
  "age": 96,
238
- "zip": 123,
239
- "registered": true
275
+ "id": "00123", // Preserved leading zeros
276
+ "zip": 123, // Converted to number
277
+ "registered": true // Converted to boolean
240
278
  },
241
279
  {
242
280
  "first_name": "Norah",
@@ -244,29 +282,12 @@ For example:
244
282
  "email": "nraison1@wired.com",
245
283
  "gender": "Female",
246
284
  "age": 32,
247
- "zip": "",
248
- "registered": false
285
+ "id": "987",
286
+ "zip": "00456", // Preserved leading zeros
287
+ "registered": false // Case-insensitive boolean conversion
249
288
  }
250
289
  ]
251
290
  ```
252
- ##### Number
253
- The property **age** is printed as
254
- ```json
255
- "age": 32
256
- ```
257
- instead of
258
- ```json
259
- "age": "32"
260
- ```
261
- ##### Boolean
262
- The property **registered** is printed as
263
- ```json
264
- "registered": true
265
- ```
266
- instead of
267
- ```json
268
- "registered": "true"
269
- ```
270
291
 
271
292
  #### Encoding
272
293
  You can read and decode files with the following encoding:
@@ -306,6 +327,30 @@ You can read and decode files with the following encoding:
306
327
  .getJsonFromCsv(fileInputName);
307
328
  ```
308
329
 
330
+ #### Working with CSV strings directly
331
+ If you have CSV content as a string (for example, from an API response or test data), you can parse it directly without writing to a file:
332
+
333
+ ```js
334
+
335
+ // Parse CSV string to array of objects
336
+ let csvString = 'firstName;lastName\nJohn;Doe\nJane;Smith';
337
+ let jsonArray = csvToJson.csvStringToJson(csvString);
338
+ // Output: [{"firstName":"John","lastName":"Doe"},{"firstName":"Jane","lastName":"Smith"}]
339
+
340
+ // Parse CSV string to JSON string (validated)
341
+ let jsonString = csvToJson.csvStringToJsonStringified(csvString);
342
+ // Output: "[\n {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\"\n },\n {\n \"firstName\": \"Jane\",\n \"lastName\": \"Smith\"\n }\n]"
343
+ ```
344
+
345
+ Both methods support all configuration options through the chaining pattern:
346
+
347
+ ```js
348
+ let jsonArray = csvToJson
349
+ .fieldDelimiter(',')
350
+ .formatValueByType()
351
+ .csvStringToJson(csvString);
352
+ ```
353
+
309
354
  ### Chaining Pattern
310
355
 
311
356
  The exposed API is implemented with the [Method Chaining Pattern](https://en.wikipedia.org/wiki/Method_chaining), which means that multiple methods can be chained, e.g.:
package/index.d.ts CHANGED
@@ -92,6 +92,14 @@ declare module 'convert-csv-to-json' {
92
92
  getJsonFromCsv(inputFileName: string): any[];
93
93
 
94
94
  csvStringToJson(csvString: string): any[];
95
+
96
+ /**
97
+ * Parses a csv string and returns a JSON string (validated)
98
+ * @param {csvString} csvString CSV content as string
99
+ * @return {string} JSON stringified result
100
+ */
101
+ csvStringToJsonStringified(csvString: string): string;
102
+
95
103
  /**
96
104
  * Parses .csv file and put its content into a file in json format.
97
105
  * @param {inputFileName} path/filename
package/index.js CHANGED
@@ -156,6 +156,18 @@ exports.csvStringToJson = function(csvString) {
156
156
  return csvToJson.csvStringToJson(csvString);
157
157
  };
158
158
 
159
+ /**
160
+ * Parses a csv string and returns a JSON string (validated)
161
+ * @param {csvString} csvString CSV content as string
162
+ * @return {string} JSON stringified result
163
+ */
164
+ exports.csvStringToJsonStringified = function(csvString) {
165
+ if (csvString === undefined || csvString === null) {
166
+ throw new Error("csvString is not defined!!!");
167
+ }
168
+ return csvToJson.csvStringToJsonStringified(csvString);
169
+ };
170
+
159
171
  /**
160
172
  * Parses .csv file and put its content into a file in json format.
161
173
  * @param {inputFileName} path/filename
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "convert-csv-to-json",
3
- "version": "3.12.0",
3
+ "version": "3.13.0",
4
4
  "description": "Convert CSV to JSON",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/src/csvToJson.js CHANGED
@@ -69,6 +69,13 @@ class CsvToJson {
69
69
  return this.csvToJson(csvString);
70
70
  }
71
71
 
72
+ csvStringToJsonStringified(csvString) {
73
+ let json = this.csvStringToJson(csvString);
74
+ let jsonStringified = JSON.stringify(json, undefined, 1);
75
+ jsonUtils.validateJson(jsonStringified);
76
+ return jsonStringified;
77
+ }
78
+
72
79
  csvToJson(parsedCsv) {
73
80
  this.validateInputConfig();
74
81
  let lines = parsedCsv.split(newLine);
@@ -1,40 +1,109 @@
1
1
  'use strict';
2
2
 
3
3
  class StringUtils {
4
+ // Regular expressions as constants for better maintainability
5
+ static PATTERNS = {
6
+ INTEGER: /^-?\d+$/,
7
+ FLOAT: /^-?\d*\.\d+$/,
8
+ WHITESPACE: /\s/g
9
+ };
4
10
 
5
- trimPropertyName(isTrimHeaderFieldWhiteSpace,value) {
6
- if(isTrimHeaderFieldWhiteSpace) {
7
- return value.replace(/\s/g, '');
11
+ static BOOLEAN_VALUES = {
12
+ TRUE: 'true',
13
+ FALSE: 'false'
14
+ };
15
+
16
+ /**
17
+ * Removes whitespace from property names based on configuration
18
+ * @param {boolean} shouldTrimAll - If true, removes all whitespace, otherwise only trims edges
19
+ * @param {string} propertyName - The property name to process
20
+ * @returns {string} The processed property name
21
+ */
22
+ trimPropertyName(shouldTrimAll, propertyName) {
23
+ if (!propertyName) {
24
+ return '';
8
25
  }
9
- return value.trim();
10
-
26
+ return shouldTrimAll ?
27
+ propertyName.replace(StringUtils.PATTERNS.WHITESPACE, '') :
28
+ propertyName.trim();
11
29
  }
12
30
 
31
+ /**
32
+ * Converts a string value to its appropriate type while preserving data integrity
33
+ * @param {string} value - The input value to convert
34
+ * @returns {string|number|boolean} The converted value
35
+ */
13
36
  getValueFormatByType(value) {
14
- if(value === undefined || value === ''){
37
+ if (this.isEmpty(value)) {
15
38
  return String();
16
39
  }
17
- //is Number
18
- let isNumber = !isNaN(value);
19
- if (isNumber) {
20
- return Number(value);
40
+
41
+ if (this.isBoolean(value)) {
42
+ return this.convertToBoolean(value);
43
+ }
44
+
45
+ if (this.isInteger(value)) {
46
+ return this.convertInteger(value);
21
47
  }
22
- // is Boolean
23
- if(value === "true" || value === "false"){
24
- return JSON.parse(value.toLowerCase());
48
+
49
+ if (this.isFloat(value)) {
50
+ return this.convertFloat(value);
25
51
  }
52
+
26
53
  return String(value);
27
54
  }
28
55
 
29
- hasContent(values) {
30
- if (values.length > 0) {
31
- for (let i = 0; i < values.length; i++) {
32
- if (values[i]) {
33
- return true;
34
- }
35
- }
56
+ /**
57
+ * Checks if a value array contains any non-empty values
58
+ * @param {Array} values - Array to check for content
59
+ * @returns {boolean} True if array has any non-empty values
60
+ */
61
+ hasContent(values = []) {
62
+ return Array.isArray(values) &&
63
+ values.some(value => Boolean(value));
64
+ }
65
+
66
+ // Private helper methods for type checking and conversion
67
+ isEmpty(value) {
68
+ return value === undefined || value === '';
69
+ }
70
+
71
+ isBoolean(value) {
72
+ const normalizedValue = value.toLowerCase();
73
+ return normalizedValue === StringUtils.BOOLEAN_VALUES.TRUE ||
74
+ normalizedValue === StringUtils.BOOLEAN_VALUES.FALSE;
75
+ }
76
+
77
+ isInteger(value) {
78
+ return StringUtils.PATTERNS.INTEGER.test(value);
79
+ }
80
+
81
+ isFloat(value) {
82
+ return StringUtils.PATTERNS.FLOAT.test(value);
83
+ }
84
+
85
+ hasLeadingZero(value) {
86
+ const isPositiveWithLeadingZero = value.length > 1 && value[0] === '0';
87
+ const isNegativeWithLeadingZero = value.length > 2 && value[0] === '-' && value[1] === '0';
88
+ return isPositiveWithLeadingZero || isNegativeWithLeadingZero;
89
+ }
90
+
91
+ convertToBoolean(value) {
92
+ return JSON.parse(value.toLowerCase());
93
+ }
94
+
95
+ convertInteger(value) {
96
+ if (this.hasLeadingZero(value)) {
97
+ return String(value);
36
98
  }
37
- return false;
99
+
100
+ const num = Number(value);
101
+ return Number.isSafeInteger(num) ? num : String(value);
102
+ }
103
+
104
+ convertFloat(value) {
105
+ const num = Number(value);
106
+ return Number.isFinite(num) ? num : String(value);
38
107
  }
39
108
  }
40
109