mirror of
https://github.com/gchq/CyberChef
synced 2024-11-15 00:57:08 +00:00
Merge branch 'n1073645-JSONTOCSV'
This commit is contained in:
commit
cf532f1e30
2 changed files with 115 additions and 30 deletions
|
@ -6,6 +6,8 @@
|
|||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import * as flat from "flat";
|
||||
const flatten = flat.default ? flat.default.flatten : flat.flatten;
|
||||
|
||||
/**
|
||||
* JSON to CSV operation
|
||||
|
@ -38,6 +40,40 @@ class JSONToCSV extends Operation {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON to csv equivalent.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
toCsv() {
|
||||
const self = this;
|
||||
// If the JSON is an array of arrays, this is easy
|
||||
if (this.flattened[0] instanceof Array) {
|
||||
return this.flattened
|
||||
.map(row => row
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(this.cellDelim)
|
||||
)
|
||||
.join(this.rowDelim) +
|
||||
this.rowDelim;
|
||||
}
|
||||
|
||||
// If it's an array of dictionaries...
|
||||
const header = Object.keys(this.flattened[0]);
|
||||
return header
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(this.cellDelim) +
|
||||
this.rowDelim +
|
||||
this.flattened
|
||||
.map(row => header
|
||||
.map(h => row[h])
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(this.cellDelim)
|
||||
)
|
||||
.join(this.rowDelim) +
|
||||
this.rowDelim;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JSON} input
|
||||
* @param {Object[]} args
|
||||
|
@ -49,42 +85,25 @@ class JSONToCSV extends Operation {
|
|||
// Record values so they don't have to be passed to other functions explicitly
|
||||
this.cellDelim = cellDelim;
|
||||
this.rowDelim = rowDelim;
|
||||
const self = this;
|
||||
|
||||
if (!(input instanceof Array)) {
|
||||
input = [input];
|
||||
this.flattened = input;
|
||||
if (!(this.flattened instanceof Array)) {
|
||||
this.flattened = [input];
|
||||
}
|
||||
|
||||
try {
|
||||
// If the JSON is an array of arrays, this is easy
|
||||
if (input[0] instanceof Array) {
|
||||
return input
|
||||
.map(row => row
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(cellDelim)
|
||||
)
|
||||
.join(rowDelim) +
|
||||
rowDelim;
|
||||
return this.toCsv();
|
||||
} catch (err) {
|
||||
try {
|
||||
this.flattened = flatten(input);
|
||||
if (!(this.flattened instanceof Array)) {
|
||||
this.flattened = [this.flattened];
|
||||
}
|
||||
|
||||
// If it's an array of dictionaries...
|
||||
const header = Object.keys(input[0]);
|
||||
return header
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(cellDelim) +
|
||||
rowDelim +
|
||||
input
|
||||
.map(row => header
|
||||
.map(h => row[h])
|
||||
.map(self.escapeCellContents.bind(self))
|
||||
.join(cellDelim)
|
||||
)
|
||||
.join(rowDelim) +
|
||||
rowDelim;
|
||||
return this.toCsv();
|
||||
} catch (err) {
|
||||
throw new OperationError("Unable to parse JSON to CSV: " + err.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Correctly escapes a cell's contents based on the cell and row delimiters.
|
||||
|
|
|
@ -89,5 +89,71 @@ TestRegister.addTests([
|
|||
args: [",", "\\r\\n"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: nested JSON",
|
||||
input: JSON.stringify({a: 1, b: {c: 2, d: 3}}),
|
||||
expectedOutput: "a,b.c,b.d\r\n1,2,3\r\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\\r\\n"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: nested array",
|
||||
input: JSON.stringify({a: 1, b: [2, 3]}),
|
||||
expectedOutput: "a,b.0,b.1\r\n1,2,3\r\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\\r\\n"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: nested JSON, nested array",
|
||||
input: JSON.stringify({a: 1, b: {c: [2, 3], d: 4}}),
|
||||
expectedOutput: "a,b.c.0,b.c.1,b.d\r\n1,2,3,4\r\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\\r\\n"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: nested array, nested JSON",
|
||||
input: JSON.stringify({a: 1, b: [{c: 3, d: 4}]}),
|
||||
expectedOutput: "a,b.0.c,b.0.d\r\n1,3,4\r\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\\r\\n"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: nested array, nested array",
|
||||
input: JSON.stringify({a: 1, b: [[2, 3]]}),
|
||||
expectedOutput: "a,b.0.0,b.0.1\r\n1,2,3\r\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\\r\\n"]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "JSON to CSV: nested JSON, nested JSON",
|
||||
input: JSON.stringify({a: 1, b: { c: { d: 2, e: 3}}}),
|
||||
expectedOutput: "a,b.c.d,b.c.e\r\n1,2,3\r\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "JSON to CSV",
|
||||
args: [",", "\\r\\n"]
|
||||
},
|
||||
],
|
||||
}
|
||||
]);
|
||||
|
|
Loading…
Reference in a new issue