Fix CSV node to handle headers with quotes and spaces

This is a breaking change so needs thinking about
Includes updated tests
This commit is contained in:
Dave Conway-Jones
2022-10-19 09:19:54 +01:00
parent 5b27bcd781
commit 501c78666d
2 changed files with 58 additions and 36 deletions

View File

@@ -38,16 +38,36 @@ module.exports = function(RED) {
if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true;
var node = this;
var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
// var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
// pass in an array of column names to be trimmed, de-quoted and retrimmed
var clean = function(col,sep) {
if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); }
col = col.trim().split(re) || [""];
col = col.map(x => x.replace(/"/g,'').trim());
if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
var ff = true; // flag to indicate if inside or outside a pair of quotes true = outside.
var jj = 0; // pointer into array of template items
var kk = [""]; // array of data for each of the template items
for (var ii = 0; ii < col.length; ii++) {
if (col[ii] === node.quo) { // if it's a quote toggle inside or outside
if (ii === 0 || col[ii-1] === sep) { ff = !ff; }
else if (col[ii-1] === node.quo) { } // do nothing, "" = " in CSV world
else if (!ff && kk[jj][0] !== node.quo) { ff = !ff; }
else { kk[jj] += col[ii]; }
}
else if ((col[ii] === sep) && ff) { // if it is the end of the group then finish
jj += 1;
ff = true;
kk[jj] = col.length - 1 === ii ? null : "";
}
else if (col[ii] === " " && ff && (ii == 0 | col[ii-1] == sep | col[ii+1] == sep)) {
// skip
}
else {
kk[jj] += col[ii];
}
}
if ((kk.length === 1) && (kk[0] === "")) { node.goodtmpl = false; }
else { node.goodtmpl = true; }
return col;
return kk;
}
var template = clean(node.template,',');
var notemplate = template.length === 1 && template[0] === '';
@@ -61,7 +81,7 @@ module.exports = function(RED) {
if (typeof msg.payload == "object") { // convert object to CSV string
try {
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
template = clean(node.template);
template = clean(node.template,",");
}
var ou = "";
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
@@ -136,7 +156,10 @@ module.exports = function(RED) {
}
else {
var tt = template[t];
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
if (template[t].indexOf('"') >=0 ) {
tt = tt.replaceAll("'","\\'");
tt = "'"+tt+"'";
}
else { tt = '"'+tt+'"'; }
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
/* istanbul ignore else */
@@ -199,7 +222,7 @@ module.exports = function(RED) {
if ((node.hdrin === true) && first) { // if the template is in the first line
if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break
if (line.length - i === 1) { tmp += line[i]; }
template = clean(tmp,node.sep);
template = clean(tmp.trimEnd(),node.sep);
first = false;
}
else { tmp += line[i]; }