Skip to content

Commit cc31fef

Browse files
authored
Add optional value parser (#179)
* Add an option to specify a custom value parser fn. One of the commonly requested features for both json2csv and csv2json is the ability to specify a custom value parser function. This functionality also provides a way to customize the behavior of the library as well to ensure that custom values are converted correctly when converting to the other format. * Add tests for parseValue option.
1 parent 195fd53 commit cc31fef

File tree

6 files changed

+47
-4
lines changed

6 files changed

+47
-4
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ Looking for examples? Check out the Wiki: [json-2-csv Wiki](https://github.com/m
9191
* `[ 'key1', 'key2', ... ]`
9292
* `[ { field: 'key1', title: 'Key 1' }, { field: 'key2' }, 'key3', ... ]`
9393
* Key Paths - If you are converting a nested object (ie. {info : {name: 'Mike'}}), then set this to ['info.name']
94+
* `parseValue` - Function - Specify how values should be converted into CSV format. This function is provided a single field value at a time and must return a `String`.
95+
* Default: A built-in method is used to parse out a variety of different value types to well-known formats.
96+
* Note: Using this option may override other options, including `useDateIso8601Format` and `useLocaleFormat`.
9497
* `prependHeader` - Boolean - Should the auto-generated header be prepended as the first line in the CSV?
9598
* Default: `true`
9699
* `sortHeader` - Boolean - Should the header keys be sorted in alphabetical order?
@@ -161,6 +164,8 @@ Available in version `2.2.0`, this functionality makes use of promises from the
161164
* Default: `null`
162165
* If you have a nested object (ie. `{info : {name: 'Mike'}}`), then set this to `['info.name']`
163166
* If you want all keys to be converted, then specify `null` or don't specify the option to utilize the default.
167+
* `parseValue` - Function - Specify how `String` representations of field values should be parsed when converting back to JSON. This function is provided a single `String` and can return any value.
168+
* Default: `JSON.parse` - An attempt is made to convert the String back to its original value using `JSON.parse`.
164169
* `trimHeaderFields` - Boolean - Should the header fields be trimmed?
165170
* Default: `false`
166171
* `trimFieldValues` - Boolean - Should the field values be trimmed?

lib/constants.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"expandArrayObjects": false,
3535
"unwindArrays": false,
3636
"useDateIso8601Format": false,
37-
"useLocaleFormat": false
37+
"useLocaleFormat": false,
38+
"parseValue": null
3839
},
3940

4041
"values" : {

lib/csv2json.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ let path = require('doc-path'),
66

77
const Csv2Json = function(options) {
88
const escapedWrapDelimiterRegex = new RegExp(options.delimiter.wrap + options.delimiter.wrap, 'g'),
9-
excelBOMRegex = new RegExp('^' + constants.values.excelBOM);
9+
excelBOMRegex = new RegExp('^' + constants.values.excelBOM),
10+
valueParserFn = options.parseValue && typeof options.parseValue === 'function' ? options.parseValue : JSON.parse;
1011

1112
/**
1213
* Trims the header key, if specified by the user via the provided options
@@ -355,7 +356,7 @@ const Csv2Json = function(options) {
355356
return value;
356357
}
357358

358-
let parsedJson = JSON.parse(value);
359+
let parsedJson = valueParserFn(value);
359360

360361
// If the parsed value is an array, then we also need to trim record values, if specified
361362
if (Array.isArray(parsedJson)) {

lib/json2csv.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ let path = require('doc-path'),
88
const Json2Csv = function(options) {
99
const wrapDelimiterCheckRegex = new RegExp(options.delimiter.wrap, 'g'),
1010
crlfSearchRegex = /\r?\n|\r/,
11+
valueParserFn = options.parseValue && typeof options.parseValue === 'function' ? options.parseValue : recordFieldValueToString,
1112
expandingWithoutUnwinding = options.expandArrayObjects && !options.unwindArrays,
1213
deeksOptions = {
1314
expandArrayObjects: expandingWithoutUnwinding,
@@ -246,7 +247,7 @@ const Json2Csv = function(options) {
246247
// Process the data in this record and return the
247248
processedRecordData = recordFieldData.map((fieldValue) => {
248249
fieldValue = trimRecordFieldValue(fieldValue);
249-
fieldValue = recordFieldValueToString(fieldValue);
250+
fieldValue = valueParserFn(fieldValue);
250251
fieldValue = wrapFieldValueIfNecessary(fieldValue);
251252

252253
return fieldValue;

test/csv2json.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,25 @@ function runTests(jsonTestData, csvTestData) {
385385
keys: ['arrayOfStrings', 'object.subField']
386386
});
387387
});
388+
389+
it('should use a custom value parser function when provided', (done) => {
390+
let updatedJson = jsonTestData.trimmedFields.map((doc) => {
391+
doc.arrayOfStrings = 'Parsed Value';
392+
doc.object.subField = 'Parsed Value';
393+
doc.number = 'Parsed Value';
394+
doc.isBoolean = 'Parsed Value';
395+
doc.optionalField = 'Parsed Value';
396+
return doc;
397+
});
398+
399+
converter.csv2json(csvTestData.trimmedFields, (err, json) => {
400+
if (err) done(err);
401+
json.should.deepEqual(updatedJson);
402+
done();
403+
}, {
404+
parseValue: () => 'Parsed Value'
405+
});
406+
});
388407
});
389408
});
390409

test/json2csv.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,22 @@ function runTests(jsonTestData, csvTestData) {
594594
excludeKeys: ['data.options.name']
595595
});
596596
});
597+
598+
it('should use a custom value parser function when provided', (done) => {
599+
let updatedCsv = csvTestData.trimmedFields.split('\n');
600+
const textRow = 'Parsed Value,Parsed Value,Parsed Value,Parsed Value,Parsed Value';
601+
updatedCsv[1] = textRow;
602+
updatedCsv[2] = textRow;
603+
updatedCsv = updatedCsv.join('\n');
604+
605+
converter.json2csv(jsonTestData.trimmedFields, (err, csv) => {
606+
if (err) done(err);
607+
csv.should.equal(updatedCsv);
608+
done();
609+
}, {
610+
parseValue: () => 'Parsed Value'
611+
});
612+
});
597613
});
598614
});
599615

0 commit comments

Comments
 (0)