budibase/packages/server/src/utilities/csv.ts

53 lines
1.8 KiB
TypeScript
Raw Normal View History

2023-05-02 11:34:45 +02:00
import csv from "csvtojson"
export async function jsonFromCsvString(csvString: string) {
2025-01-29 16:42:33 +01:00
const possibleDelimeters = [",", ";", ":", "|", "~", "\t", " "]
2023-05-02 12:57:18 +02:00
2025-01-29 16:42:33 +01:00
for (let i = 0; i < possibleDelimeters.length; i++) {
let headers: string[] | undefined = undefined
2025-01-29 16:42:33 +01:00
let headerMismatch = false
try {
// By default the csvtojson library casts empty values as empty strings. This
// is causing issues on conversion. ignoreEmpty will remove the key completly
// if empty, so creating this empty object will ensure we return the values
// with the keys but empty values
const result = await csv({
ignoreEmpty: false,
delimiter: possibleDelimeters[i],
}).fromString(csvString)
2025-01-29 19:28:24 +01:00
for (const [, r] of result.entries()) {
// The purpose of this is to find rows that have been split
// into the wrong number of columns - Any valid .CSV file will have
// the same number of colums in each row
// If the number of columms in each row is different to
// the number of headers, this isn't the right delimiter
const columns = Object.keys(r)
if (headers == null) {
headers = columns
}
if (headers.length === 1 || headers.length !== columns.length) {
headerMismatch = true
break
}
for (const header of headers) {
if (r[header] === undefined || r[header] === "") {
r[header] = null
}
}
2025-01-29 16:42:33 +01:00
}
if (headerMismatch) {
continue
} else {
return result
2025-01-29 16:42:33 +01:00
}
} catch (err) {
// Splitting on the wrong delimiter sometimes throws CSV parsing error
// (eg unterminated strings), which tells us we've picked the wrong delimiter
2025-01-29 16:42:33 +01:00
continue
}
}
throw new Error("Unable to determine delimiter")
2023-05-02 11:34:45 +02:00
}