Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@ $ npm run coverage

Current Coverage is:
```
Statements : 100% ( 272/272 )
Branches : 100% ( 143/143 )
Statements : 100% ( 275/275 )
Branches : 100% ( 149/149 )
Functions : 100% ( 49/49 )
Lines : 100% ( 266/266 )
Lines : 100% ( 269/269 )
```

## Frequently Asked Questions (FAQ)
Expand Down
362 changes: 201 additions & 161 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
},
"name": "json-2-csv",
"description": "A JSON to CSV and CSV to JSON converter that natively supports sub-documents and auto-generates the CSV heading.",
"version": "3.4.1",
"version": "3.4.2",
"repository": {
"type": "git",
"url": "https://github.com/mrodrig/json-2-csv.git"
Expand All @@ -21,7 +21,7 @@
"types": "./src/converter.d.ts",
"scripts": {
"test": "mocha test/tests.js",
"coverage": "istanbul cover mocha -- -R spec",
"coverage": "istanbul cover _mocha -- -R spec",
"lint": "npm run lint:eslint && npm run lint:tslint",
"lint:eslint": "eslint src bin test",
"lint:tslint": "tslint -c tslint.json 'src/**/*.ts'"
Expand All @@ -47,11 +47,11 @@
},
"devDependencies": {
"commander": "2.19.0",
"eslint": "5.11.1",
"eslint": "5.15.3",
"istanbul": "0.4.5",
"mocha": "5.2.0",
"should": "13.2.3",
"tslint": "5.12.1",
"tslint": "5.14.0",
"typescript": "3.3.3"
},
"engines": {
Expand Down
4 changes: 2 additions & 2 deletions src/converter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,6 @@ export function json2csv(data: object[],
export function json2csvAsync(data: object[], options?: IFullOptions): Promise<string>;

export function csv2json(csv: string,
callback: (err?: Error, data?: Array<unknown>) => void, options?: ISharedOptions): void;
callback: (err?: Error, data?: unknown[]) => void, options?: ISharedOptions): void;

export function csv2jsonAsync(csv: string, options?: ISharedOptions): Promise<Array<unknown>>;
export function csv2jsonAsync(csv: string, options?: ISharedOptions): Promise<unknown[]>;
39 changes: 32 additions & 7 deletions src/csv2json.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,40 +93,65 @@ const Csv2Json = function(options) {
// Next character
charAfter = index < lastCharacterIndex ? line[index + 1] : '';

if (index === lastCharacterIndex) {
// If we reached the end of the line, add the remaining value
if (index === lastCharacterIndex && character === options.delimiter.field) {
// If we reached the end of the line and the current character is a field delimiter...

// Push the value for the field that we were parsing
splitLine.push(
// If the start index is the current index (and since the character is a comma),
// then the value being parsed is an empty value accordingly, add an empty string
stateVariables.startIndex === index
? ''
// Otherwise there is a valid value, but we do not want to include the current character (field delimiter)
: line.substring(stateVariables.startIndex, index)
);

// Since the last character is a comma, there's still an additional implied field value trailing the comma.
// Since this value is empty, we push an extra empty value
splitLine.push('');
} else if (index === lastCharacterIndex) {
// Otherwise if we reached the end of the line (and current character is not a field delimiter)

// Retrieve the remaining value and add it to the split line list of values
splitLine.push(line.substring(stateVariables.startIndex));
} else if (character === options.delimiter.wrap && index === 0) {
// If the line starts with a wrap delimiter
// If the line starts with a wrap delimiter (ie. "*)

stateVariables.insideWrapDelimiter = true;
stateVariables.parsingValue = true;
stateVariables.startIndex = index;
} else if (character === options.delimiter.wrap && charAfter === options.delimiter.field) {
// If we reached a wrap delimiter with a field delimiter after it (ie. *",)

splitLine.push(line.substring(stateVariables.startIndex, index + 1));
stateVariables.startIndex = index + 2; // next value starts after the field delimiter
stateVariables.insideWrapDelimiter = false;
stateVariables.parsingValue = false;
} else if (character === options.delimiter.wrap && charBefore === options.delimiter.field && !stateVariables.insideWrapDelimiter && stateVariables.parsingValue) {
} else if (character === options.delimiter.wrap && charBefore === options.delimiter.field &&
!stateVariables.insideWrapDelimiter && stateVariables.parsingValue) {
// If we reached a wrap delimiter with a field delimiter after it (ie. ,"*)

splitLine.push(line.substring(stateVariables.startIndex, index - 1));
stateVariables.insideWrapDelimiter = true;
stateVariables.parsingValue = true;
stateVariables.startIndex = index;
} else if (character === options.delimiter.wrap && charAfter === options.delimiter.wrap) {
// If we run into an escaped quote
// If we run into an escaped quote (ie. "") skip past the second quote

index += 2;
continue;
} else if (character === options.delimiter.field && charBefore !== options.delimiter.wrap &&
// If we reached a field delimiter and are not inside the wrap delimiters (ie. *,*)
charAfter !== options.delimiter.wrap && !stateVariables.insideWrapDelimiter &&
stateVariables.parsingValue) {
// If we reached a field delimiter and are not inside the wrap delimiters (ie. *,*)

splitLine.push(line.substring(stateVariables.startIndex, index));
stateVariables.startIndex = index + 1;
} else if (character === options.delimiter.field && charBefore === options.delimiter.wrap &&
// If we reached a field delimiter, the previous character was a wrap delimiter, and the next character is not a wrap delimiter (ie. ",*)
charAfter !== options.delimiter.wrap && !stateVariables.parsingValue) {
// If we reached a field delimiter, the previous character was a wrap delimiter, and the
// next character is not a wrap delimiter (ie. ",*)

stateVariables.insideWrapDelimiter = false;
stateVariables.parsingValue = true;
stateVariables.startIndex = index + 1;
Expand Down
3 changes: 2 additions & 1 deletion test/config/testCsvFilesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ const fs = require('fs'),
{key: 'extraLine', file: '../data/csv/extraLine.csv'},
{key: 'noHeader', file: '../data/csv/noHeader.csv'},
{key: 'sortedHeader', file: '../data/csv/sortedHeader.csv'},
{key: 'emptyFieldValues', file: '../data/csv/emptyFieldValues.csv'}
{key: 'emptyFieldValues', file: '../data/csv/emptyFieldValues.csv'},
{key: 'csvEmptyLastValue', file: '../data/csv/csvEmptyLastValue.csv'}
];

function readCsvFile(filePath) {
Expand Down
3 changes: 2 additions & 1 deletion test/config/testJsonFilesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ module.exports = {
trimHeader: require('../data/json/trimHeader'),
trimmedHeader: require('../data/json/trimmedHeader'),
specifiedKeys: require('../data/json/specifiedKeys'),
emptyFieldValues: require('../data/json/emptyFieldValues')
emptyFieldValues: require('../data/json/emptyFieldValues'),
csvEmptyLastValue: require('../data/json/csvEmptyLastValue')
};
9 changes: 9 additions & 0 deletions test/csv2json.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ function runTests(jsonTestData, csvTestData) {
done();
});
});

// Test case for #109
it('should properly handle the cases involving an empty field value', (done) => {
converter.csv2json(csvTestData.csvEmptyLastValue, (err, json) => {
if (err) done(err);
json.should.deepEqual(jsonTestData.csvEmptyLastValue);
done();
});
});
});

describe('Error Handling', () => {
Expand Down
4 changes: 4 additions & 0 deletions test/data/csv/csvEmptyLastValue.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Name,Category,On Sale,On Closeout,Outsourced,Certification Required
Installation,Service,X,,,
"Wireless ""Wi-Fi"" Configuration",Service,X,,X,
New Phone Setup,Service,X,X,X,X
26 changes: 26 additions & 0 deletions test/data/json/csvEmptyLastValue.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"Name": "Installation",
"Category": "Service",
"On Sale": "X",
"On Closeout": "",
"Outsourced": "",
"Certification Required": ""
},
{
"Name": "Wireless \"Wi-Fi\" Configuration",
"Category": "Service",
"On Sale": "X",
"On Closeout": "",
"Outsourced": "X",
"Certification Required": ""
},
{
"Name": "New Phone Setup",
"Category": "Service",
"On Sale": "X",
"On Closeout": "X",
"Outsourced": "X",
"Certification Required": "X"
}
]
8 changes: 8 additions & 0 deletions test/json2csv.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ function runTests(jsonTestData, csvTestData) {
done();
});
});

it('should properly convert the cases involving an empty field value in the csv', (done) => {
converter.json2csv(jsonTestData.csvEmptyLastValue, (err, csv) => {
if (err) done(err);
csv.should.equal(csvTestData.csvEmptyLastValue);
done();
});
});
});

describe('Error Handling', () => {
Expand Down