Skip to content

Commit 5d88d0a

Browse files
committed
Merge pull request #19 from mrodrig/add-field-option
Add KEYS option to allow for specific keys to be converted
2 parents 559e6eb + beb388b commit 5d88d0a

13 files changed

+377
-35
lines changed

README.md

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ var converter = require('json-2-csv');
4040
##### json2csv Example:
4141

4242
```javascript
43-
4443
var converter = require('json-2-csv');
4544

4645
var documents = [
@@ -70,7 +69,6 @@ var json2csvCallback = function (err, csv) {
7069
};
7170

7271
converter.json2csv(documents, json2csvCallback);
73-
7472
```
7573

7674
The above code prints out:
@@ -90,7 +88,10 @@ BMW,X5,2014,3287,M
9088
* `FIELD` - String - Field Delimiter. Default: `','`
9189
* `ARRAY` - String - Array Value Delimiter. Default: `';'`
9290
* `EOL` - String - End of Line Delimiter. Default: `'\n'`
93-
* `PARSE_CSV_NUMBERS` - Boolean - Should numbers that are found in the CSV be converted to numbers? Default: `false`
91+
* `PARSE_CSV_NUMBERS` - Boolean - (TODO) Should numbers that are found in the CSV be converted to numbers? Default: `false`
92+
* `KEYS` - Array - Specify the keys (as strings) that should be converted. Default: `null`
93+
* If you have a nested object (ie. {info : {name: 'Mike'}}), then set options.KEYS to ['info.name']
94+
* If you want all keys to be converted, then specify ```null``` or don't specify the option to utilize the default.
9495

9596
##### csv2json Example:
9697

@@ -137,19 +138,72 @@ _Note_: This requires `mocha`, `should`, `async`, and `underscore`.
137138
## Features
138139

139140
- Header Generation (per document keys)
141+
- Allows for conversion of specific keys in both json2csv and csv2json via the options.KEYS parameter (as of 1.1.2)
140142
- Verifies all documents have same schema (schema field order does not matter as of 1.1.0)
141143
- Supports sub-documents natively
142144
- Supports arrays as document values for both json2csv and csv2json
143145
- Custom ordering of columns (see F.A.Q. for more information)
144146
- Ability to re-generate the JSON documents that were used to generate the CSV (including nested documents)
145147
- Allows for custom field delimiters, end of line delimiters, etc.
146-
- Promisifiable via bluebird's .promisify(<function) and .promisifyAll() (as of 1.1.1)
148+
- Promisifiable via bluebird's .promisify(<function>) and .promisifyAll(<object>) (as of 1.1.1)
147149

148150
## F.A.Q.
149151

150152
- Can the order of the keys be changed in the output?
151153
__Yes.__ Currently, changing the order of the keys in the JSON document will also change the order of the columns. (Tested on Node 10.xx)
152154

155+
- Can I specify the keys that I would like to have converted to CSV or JSON?
156+
__Yes.__ This is currently supported for both json2csv and csv2json. Specify the keys in options.KEYS. For example,
157+
158+
```javascript
159+
var converter = require('json-2-csv');
160+
161+
var options = {
162+
KEYS : ['info.name', 'year']
163+
};
164+
165+
var documents = [
166+
{
167+
"info": {
168+
"name": "Mike"
169+
},
170+
"coursesTaken": ["CS2500", "CS2510"],
171+
"year": "Sophomore"
172+
},
173+
{
174+
"info": {
175+
"name": "John"
176+
},
177+
"coursesTaken": ["ANTH1101", "POL2312", "MATH2142", "POL3305", "LAW2100"],
178+
"year": "Senior"
179+
},
180+
{
181+
"info": {
182+
"name": "Joe"
183+
},
184+
"coursesTaken": [],
185+
"year": "Freshman"
186+
}
187+
];
188+
189+
converter.json2csv(documents, function (err, csv) {
190+
if (!err) {
191+
return console.log(csv);
192+
}
193+
throw err;
194+
}, options);
195+
```
196+
197+
This prints out:
198+
199+
```csv
200+
info.name,year
201+
Mike,Sophomore
202+
John,Senior
203+
Joe,Freshman
204+
205+
```
206+
153207
## Milestones
154208
- Created: Apr 23, 2014
155209
- 1K Downloads/Month: January 15, 2015

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "json-2-csv",
3-
"version": "1.1.1",
3+
"version": "1.1.2",
44
"homepage": "https://github.com/mrodrig/json-2-csv",
55
"moduleType": [
66
"node"

lib/converter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ var defaultOptions = {
1414
WRAP : ''
1515
},
1616
EOL : '\n',
17-
PARSE_CSV_NUMBERS : false
17+
PARSE_CSV_NUMBERS : false,
18+
KEYS : null
1819
};
1920

2021
/**

lib/csv-2-json.js

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ var retrieveHeading = function (lines, callback) {
99
if (!lines.length) { // If there are no lines passed in, then throw an error
1010
return callback(new Error("No data provided to retrieve heading.")); // Pass an error back to the user
1111
}
12-
var heading = lines[0]; // Grab the top line (header line)
13-
return heading.split(options.DELIMITER.FIELD); // Return the heading split by the field delimiter
12+
var heading = lines[0].split(options.DELIMITER.FIELD); // Grab the top line (header line) and split by the field delimiter
13+
heading = _.map(heading, function (headerKey, index) {
14+
return {
15+
value: headerKey,
16+
index: index
17+
}
18+
});
19+
return heading;
1420
};
1521

1622
// Add a nested key and its value in the given document
@@ -53,21 +59,18 @@ var convertArrayRepresentation = function (val) {
5359
};
5460

5561
// Create a JSON document with the given keys (designated by the CSV header) and the values (from the given line)
56-
var createDoc = function (keys, line, callback) {
62+
var createDoc = function (keys, line) {
63+
if (line == '') { return false; } // If we have an empty line, then return false so we can remove all blank lines (falsy values)
5764
var doc = {}, // JSON document to start with and manipulate
5865
val, // Temporary variable to set the current key's value to
5966
line = line.trim().split(options.DELIMITER.FIELD); // Split the line using the given field delimiter after trimming whitespace
60-
if (line == '') { return false; } // If we have an empty line, then return false so we can remove all blank lines (falsy values)
61-
if (keys.length !== line.length) { // If the number of keys is different than the number of values in the current line
62-
return callback(new Error("Not every line has a correct number of values.")); // Pass the error back to the client
63-
}
6467
_.each(keys, function (key, indx) {
65-
val = line[indx] === '' ? null : line[indx];
68+
val = line[key.index] === '' ? null : line[key.index];
6669
if (isArrayRepresentation(val)) {
6770
val = convertArrayRepresentation(val);
6871
}
69-
if (key.indexOf('.')) { // If key has '.' representing nested document
70-
doc = addNestedKey(key, val, doc); // Update the document to add the nested key structure
72+
if (key.value.indexOf('.')) { // If key has '.' representing nested document
73+
doc = addNestedKey(key.value, val, doc); // Update the document to add the nested key structure
7174
} else { // Else we just have a straight key:value mapping
7275
doc[key] = val; // Set the value at the current key
7376
}
@@ -77,13 +80,16 @@ var createDoc = function (keys, line, callback) {
7780

7881
// Main wrapper function to convert the CSV to the JSON document array
7982
var convertCSV = function (lines, callback) {
80-
var headers = retrieveHeading(lines, callback), // Retrieve the headings from the CSV
81-
jsonDocs = []; // Create an array that we can add the generated documents to
83+
var generatedHeaders = retrieveHeading(lines, callback), // Retrieve the headings from the CSV, unless the user specified the keys
84+
jsonDocs = [], // Create an array that we can add the generated documents to
85+
headers = options.KEYS ? _.filter(generatedHeaders, function (headerKey) {
86+
return _.contains(options.KEYS, headerKey.value);
87+
}) : generatedHeaders;
8288
lines = lines.splice(1); // Grab all lines except for the header
8389
_.each(lines, function (line) { // For each line, create the document and add it to the array of documents
8490
jsonDocs.push(createDoc(headers, line));
8591
});
86-
return _.filter(jsonDocs, function (doc) { return doc !== false; });; // Return all non 'falsey' values to filter blank lines
92+
return _.filter(jsonDocs, function (doc) { return doc !== false; }); // Return all non 'falsey' values to filter blank lines
8793
};
8894

8995
module.exports = {
@@ -103,4 +109,4 @@ module.exports = {
103109
callback(null, json); // Send the data back to the caller
104110
}
105111

106-
};
112+
};

lib/json-2-csv.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var options = {}; // Initialize the options - this will be populated when the js
88
// Retrieve the headings for all documents and return it. This checks that all documents have the same schema.
99
var generateHeading = function(data) {
1010
return new Promise(function (resolve, reject) {
11+
if (options.KEYS) { resolve(options.KEYS); }
1112
var keys = _.map(_.keys(data), function (key, indx) { // for each key
1213
if (_.isObject(data[key])) {
1314
// if the data at the key is a document, then we retrieve the subHeading starting with an empty string heading and the doc

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"author": "mrodrig",
33
"name": "json-2-csv",
44
"description": "A JSON to CSV and CSV to JSON converter that natively supports sub-documents and auto-generates the CSV heading.",
5-
"version": "1.1.1",
5+
"version": "1.1.2",
66
"repository": {
77
"type": "git",
88
"url": "http://github.com/mrodrig/json-2-csv.git"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
info.name,year
2+
Mike,Sophomore
3+
John,Senior
4+
Joe,Freshman
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"info.name","year"
2+
"Mike","Sophomore"
3+
"John","Senior"
4+
"Joe","Freshman"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[
2+
{
3+
"info": {
4+
"name": "Mike"
5+
},
6+
"year": "Sophomore"
7+
},
8+
{
9+
"info": {
10+
"name": "John"
11+
},
12+
"year": "Senior"
13+
},
14+
{
15+
"info": {
16+
"name": "Joe"
17+
},
18+
"year": "Freshman"
19+
}
20+
]

test/testComma.js

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var json_regularJson = require('./JSON/regularJson'),
2121
json_noData = require('./JSON/noData.json'),
2222
json_singleDoc = require('./JSON/singleDoc.json'),
2323
json_arrayValue = require('./JSON/arrayValueDocs.json'),
24+
json_arrayValue_specificKeys = require('./JSON/arrayValueDocs_specificKeys.json'),
2425
json_sameSchemaDifferentOrdering = require('./JSON/sameSchemaDifferentOrdering'),
2526
json_differentSchemas = require('./JSON/differentSchemas'),
2627
csv_regularJson = '',
@@ -29,7 +30,8 @@ var json_regularJson = require('./JSON/regularJson'),
2930
csv_nestedQuotes = '',
3031
csv_noData = '',
3132
csv_singleDoc = '',
32-
csv_arrayValue = '';
33+
csv_arrayValue = '',
34+
csv_arrayValue_specificKeys = '';
3335

3436
var json2csvTests = function () {
3537
describe('json2csv - non-promisified', function (done) {
@@ -90,6 +92,16 @@ var json2csvTests = function () {
9092
}, options);
9193
});
9294

95+
it('should parse the specified keys to CSV', function (done) {
96+
// Create a copy so we don't modify the actual options object
97+
var opts = _.extend(JSON.parse(JSON.stringify(options)), {KEYS: ['info.name', 'year']});
98+
converter.json2csv(json_arrayValue, function (err, csv) {
99+
csv.should.equal(csv_arrayValue_specificKeys);
100+
csv.split(options.EOL).length.should.equal(5);
101+
done();
102+
}, opts);
103+
});
104+
93105
it('should parse an array of JSON documents with the same schema but different ordering of fields', function (done) {
94106
converter.json2csv(json_sameSchemaDifferentOrdering, function (err, csv) {
95107
csv.should.equal(csv_regularJson);
@@ -510,6 +522,20 @@ var json2csvTests = function () {
510522
});
511523
});
512524

525+
it('should parse the specified keys to CSV', function (done) {
526+
// Create a copy so we don't modify the actual options object
527+
var opts = _.extend(JSON.parse(JSON.stringify(options)), {KEYS: ['info.name', 'year']});
528+
converter.json2csvAsync(json_arrayValue, opts)
529+
.then(function (csv) {
530+
csv.should.equal(csv_arrayValue_specificKeys);
531+
csv.split(options.EOL).length.should.equal(5);
532+
done();
533+
})
534+
.catch(function (err) {
535+
throw err;
536+
});
537+
});
538+
513539
it('should parse an array of JSON documents with the same schema but different ordering of fields', function (done) {
514540
converter.json2csvAsync(json_sameSchemaDifferentOrdering)
515541
.then(function (csv) {
@@ -617,6 +643,15 @@ var csv2jsonTests = function () {
617643
}, options);
618644
});
619645

646+
it('should parse the specified keys to JSON', function (done) {
647+
var opts = _.extend(JSON.parse(JSON.stringify(options)), {KEYS : ['info.name', 'year']});
648+
converter.csv2jsonAsync(csv_arrayValue.replace(/,/g, options.DELIMITER.FIELD), function (err, json) {
649+
var isEqual = _.isEqual(json, json_arrayValue_specificKeys);
650+
true.should.equal(isEqual);
651+
done();
652+
}, opts);
653+
});
654+
620655
it('should throw an error about not having been passed data - 1', function (done) {
621656
converter.csv2json(null, function (err, json) {
622657
err.message.should.equal('Cannot call csv2json on null.');
@@ -876,6 +911,19 @@ var csv2jsonTests = function () {
876911
});
877912
});
878913

914+
it('should parse the specified keys to JSON', function (done) {
915+
var opts = _.extend(JSON.parse(JSON.stringify(options)), {KEYS : ['info.name', 'year']});
916+
converter.csv2jsonAsync(csv_arrayValue.replace(/,/g, options.DELIMITER.FIELD), opts)
917+
.then(function (json) {
918+
var isEqual = _.isEqual(json, json_arrayValue_specificKeys);
919+
true.should.equal(isEqual);
920+
done();
921+
})
922+
.catch(function (err) {
923+
throw err;
924+
});
925+
});
926+
879927
it('should throw an error about not having been passed data - 1', function (done) {
880928
converter.csv2jsonAsync(null, options)
881929
.then(function (json) {
@@ -1063,6 +1111,13 @@ module.exports = {
10631111
csv_arrayValue = data.toString();
10641112
callback(null);
10651113
});
1114+
},
1115+
function(callback) {
1116+
fs.readFile('test/CSV/arrayValueDocs_specificKeys.csv', function (err, data) {
1117+
if (err) callback(err);
1118+
csv_arrayValue_specificKeys = data.toString();
1119+
callback(null);
1120+
});
10661121
}
10671122
],
10681123
function(err, results) {

0 commit comments

Comments
 (0)