mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-02 14:11:02 +01:00
Merge branch 'n1073645-JSONTOCSV'
This commit is contained in:
commit
cf532f1e30
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
import Operation from "../Operation.mjs";
|
import Operation from "../Operation.mjs";
|
||||||
import OperationError from "../errors/OperationError.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
|
* 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 {JSON} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
@ -49,40 +85,23 @@ class JSONToCSV extends Operation {
|
|||||||
// Record values so they don't have to be passed to other functions explicitly
|
// Record values so they don't have to be passed to other functions explicitly
|
||||||
this.cellDelim = cellDelim;
|
this.cellDelim = cellDelim;
|
||||||
this.rowDelim = rowDelim;
|
this.rowDelim = rowDelim;
|
||||||
const self = this;
|
this.flattened = input;
|
||||||
|
if (!(this.flattened instanceof Array)) {
|
||||||
if (!(input instanceof Array)) {
|
this.flattened = [input];
|
||||||
input = [input];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If the JSON is an array of arrays, this is easy
|
return this.toCsv();
|
||||||
if (input[0] instanceof Array) {
|
|
||||||
return input
|
|
||||||
.map(row => row
|
|
||||||
.map(self.escapeCellContents.bind(self))
|
|
||||||
.join(cellDelim)
|
|
||||||
)
|
|
||||||
.join(rowDelim) +
|
|
||||||
rowDelim;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError("Unable to parse JSON to CSV: " + err.toString());
|
try {
|
||||||
|
this.flattened = flatten(input);
|
||||||
|
if (!(this.flattened instanceof Array)) {
|
||||||
|
this.flattened = [this.flattened];
|
||||||
|
}
|
||||||
|
return this.toCsv();
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError("Unable to parse JSON to CSV: " + err.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,5 +89,71 @@ TestRegister.addTests([
|
|||||||
args: [",", "\\r\\n"]
|
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 New Issue
Block a user