Skip to content

Commit a608a38

Browse files
authored
Handle nested EOL delimiter inside wrap delimiter properly (#215)
* chore(test): Add test case for #214 * Handle nested newlines in quoted fields. As reported in #214, there was a bug with how the CSV parser was handling quoted fields which had newlines (or EOL delimiters) embedded in them. Since the value is wrapped in quotes, the EOL delimiter should not be interpreted as though it's a new line or the start of another field as it previously was. This commit introduces a fix to ensure that quoted fields are parsed properly regardless of whether there is a nested EOL delimiter inside. Fixes #214
1 parent e65ebb0 commit a608a38

File tree

6 files changed

+29
-4
lines changed

6 files changed

+29
-4
lines changed

lib/csv2json.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ const Csv2Json = function(options) {
162162
stateVariables.insideWrapDelimiter = false;
163163
stateVariables.parsingValue = false;
164164
// Next iteration will substring, add the value to the line, and push the line onto the array of lines
165-
} else if (character === options.delimiter.wrap && (index === 0 || utils.getNCharacters(csv, index - eolDelimiterLength, eolDelimiterLength) === options.delimiter.eol)) {
165+
} else if (character === options.delimiter.wrap && (index === 0 || utils.getNCharacters(csv, index - eolDelimiterLength, eolDelimiterLength) === options.delimiter.eol && !stateVariables.insideWrapDelimiter)) {
166166
// If the line starts with a wrap delimiter (ie. "*)
167167

168168
stateVariables.insideWrapDelimiter = true;
@@ -190,7 +190,7 @@ const Csv2Json = function(options) {
190190
stateVariables.insideWrapDelimiter = true;
191191
stateVariables.parsingValue = true;
192192
stateVariables.startIndex = index;
193-
} else if (character === options.delimiter.wrap && charAfter === options.delimiter.wrap) {
193+
} else if (character === options.delimiter.wrap && charAfter === options.delimiter.wrap && index !== stateVariables.startIndex) {
194194
// If we run into an escaped quote (ie. "") skip past the second quote
195195

196196
index += 2;

test/config/testCsvFilesList.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ const fs = require('fs'),
4444
{key: 'nestedDotKeys', file: '../data/csv/nestedDotKeys.csv'},
4545
{key: 'nestedDotKeysWithArray', file: '../data/csv/nestedDotKeysWithArray.csv'},
4646
{key: 'nestedDotKeysWithArrayExpandedUnwound', file: '../data/csv/nestedDotKeysWithArrayExpandedUnwound.csv'},
47-
{key: 'emptyColumns', file: '../data/csv/emptyColumns.csv'}
47+
{key: 'emptyColumns', file: '../data/csv/emptyColumns.csv'},
48+
{key: 'quotedFieldWithNewline', file: '../data/csv/quotedFieldWithNewline.csv'}
4849
];
4950

5051
function readCsvFile(filePath) {

test/config/testJsonFilesList.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ module.exports = {
3434
nestedDotKeys: require('../data/json/nestedDotKeys.json'),
3535
nestedDotKeysWithArray: require('../data/json/nestedDotKeysWithArray.json'),
3636
nestedDotKeysWithArrayExpandedUnwound: require('../data/json/nestedDotKeysWithArrayExpandedUnwound.json'),
37-
emptyColumns: require('../data/json/emptyColumns.json')
37+
emptyColumns: require('../data/json/emptyColumns.json'),
38+
quotedFieldWithNewline: require('../data/json/quotedFieldWithNewline.json')
3839
};

test/csv2json.js

+10
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,23 @@ function runTests(jsonTestData, csvTestData) {
226226
});
227227
});
228228

229+
// Test case for #204
229230
it('should drop any values with empty column headers', (done) => {
230231
converter.csv2json(csvTestData.emptyColumns, (err, json) => {
231232
if (err) done(err);
232233
json.should.deepEqual(jsonTestData.emptyColumns);
233234
done();
234235
});
235236
});
237+
238+
// Test case for #214
239+
it('should handle quoted fields with nested EOL characters', (done) => {
240+
converter.csv2json(csvTestData.quotedFieldWithNewline, (err, json) => {
241+
if (err) done(err);
242+
json.should.deepEqual(jsonTestData.quotedFieldWithNewline);
243+
done();
244+
});
245+
});
236246
});
237247

238248
describe('Error Handling', () => {
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
first,second,third
2+
"hello
3+
",world,test
4+
first,"world
5+
6+
",test
7+
world,test,"hello
8+
"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{ "first": "hello\n", "second": "world", "third": "test" },
3+
{ "first": "first", "second": "world\n\n", "third": "test"},
4+
{ "first": "world", "second": "test", "third": "hello\n"}
5+
]

0 commit comments

Comments
 (0)