├── .gitignore ├── LICENSE ├── README.md ├── json2csv.js ├── package.json ├── test-browser.html └── test-node.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Martin Drapeau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CSVJSON json2csv() function 2 | =========================== 3 | 4 | Single function `json2csv` to convert JSON to CSV. Self contained without dependencies. Used to power CSVJSON the online tool found at [www.csvjson.com/json2csv](https://www.csvjson.com/json2csv). Used by thousands everyday. 5 | 6 | [npm package here](https://www.npmjs.com/package/csvjson-json2csv) 7 | 8 | # Usage 9 | 10 | Simply call `json2csv(json, options)` passing a JavaScript object. Use the `options` hash to pass on some switches: 11 | - `separator`: Character which acts as separator. For semi-colon pass `;`. For TSV pass a tab `\t`. Default is `,`. 12 | - `flatten`: Boolean indicating whether to flatten nested arrays or not. Default is `false`. 13 | - `output_csvjson_variant`: Boolean indicating whether to output objects and arrays as is as per the [CSVJSON format variant](http://csvjson.org/). Default is `false`. 14 | 15 | ## Node example 16 | 17 | ``` 18 | const json2csv = require('./json2csv.js'); 19 | 20 | const obj = [ 21 | { 22 | "album": "The White Stripes", 23 | "year": 1999, 24 | "US_peak_chart_post": "-" 25 | }, 26 | { 27 | "album": "De Stijl", 28 | "year": 2000, 29 | "US_peak_chart_post": "-" 30 | }, 31 | { 32 | "album": "White Blood Cells", 33 | "year": 2001, 34 | "US_peak_chart_post": 61 35 | }, 36 | { 37 | "album": "Elephant", 38 | "year": 2003, 39 | "US_peak_chart_post": 6 40 | }, 41 | { 42 | "album": "Get Behind Me Satan", 43 | "year": 2005, 44 | "US_peak_chart_post": 3 45 | }, 46 | { 47 | "album": "Icky Thump", 48 | "year": 2007, 49 | "US_peak_chart_post": 2 50 | }, 51 | { 52 | "album": "Under Great White Northern Lights", 53 | "year": 2010, 54 | "US_peak_chart_post": 11 55 | }, 56 | { 57 | "album": "Live in Mississippi", 58 | "year": 2011, 59 | "US_peak_chart_post": "-" 60 | }, 61 | { 62 | "album": "Live at the Gold Dollar", 63 | "year": 2012, 64 | "US_peak_chart_post": "-" 65 | }, 66 | { 67 | "album": "Nine Miles from the White City", 68 | "year": 2013, 69 | "US_peak_chart_post": "-" 70 | } 71 | ]; 72 | 73 | var csv = json2csv(obj); 74 | console.log(csv); 75 | 76 | ``` 77 | 78 | ## Browser example 79 | 80 | Note: In the browser, global namespace `CSVJSON` is created. It contains the `json2csv` function. 81 | 82 | ``` 83 | 84 | 141 | ``` 142 | 143 | In both cases, you would get this in the console: 144 | ``` 145 | "album","year","US_peak_chart_post" 146 | "The White Stripes",1999,"-" 147 | "De Stijl",2000,"-" 148 | "White Blood Cells",2001,61 149 | "Elephant",2003,6 150 | "Get Behind Me Satan",2005,3 151 | "Icky Thump",2007,2 152 | "Under Great White Northern Lights",2010,11 153 | "Live in Mississippi",2011,"-" 154 | "Live at the Gold Dollar",2012,"-" 155 | "Nine Miles from the White City",2013,"-" 156 | ``` 157 | 158 | You can of course test all options online on [www.csvjson.com/json2csv](https://www.csvjson.com/json2csv). 159 | 160 | ## Flattening 161 | 162 | By default `json2csv` assumes you have an array of objects. If your array containes nested objects, use the `flatten` option. For example: 163 | 164 | ``` 165 | const json2csv = require('./json2csv.js'); 166 | const obj = { 167 | "trees": [ 168 | { 169 | "id": 1, 170 | "name": "tree1", 171 | "branches": [ 172 | { 173 | "id": 1, 174 | "name": "branch1", 175 | "leaves": [ 176 | { 177 | "id": 1, 178 | "name": "leave1" 179 | }, 180 | { 181 | "id": 2, 182 | "name": "leave2" 183 | } 184 | ] 185 | }, 186 | { 187 | "id": 2, 188 | "name": "branch2", 189 | "leaves": [ 190 | { 191 | "id": 1, 192 | "name": "leave1" 193 | }, 194 | { 195 | "id": 2, 196 | "name": "leave2" 197 | } 198 | ] 199 | } 200 | ] 201 | } 202 | ] 203 | }; 204 | 205 | var csv = json2csv(obj, {flatten: true}); 206 | console.log(csv); 207 | ``` 208 | 209 | Will produce this: 210 | 211 | ``` 212 | "trees.id","trees.name","trees.branches.id","trees.branches.name","trees.branches.leaves.id","trees.branches.leaves.name" 213 | 1,"tree1",1,"branch1",1,"leave1" 214 | 1,"tree1",1,"branch1",2,"leave2" 215 | 1,"tree1",2,"branch2",1,"leave1" 216 | 1,"tree1",2,"branch2",2,"leave2" 217 | ``` 218 | 219 | ## Tests 220 | Run the tests in your browser by opening `test-browser.html`. 221 | 222 | Run the tests through node: 223 | ``` 224 | node test-node.js 225 | ``` 226 | 227 | # Companion functions 228 | 229 | [csv2json](https://github.com/martindrapeau/csvjson-csv2json) to convert CSV to JSON. [npm package here](https://www.npmjs.com/package/csvjson-csv2json). 230 | 231 | [json_beautifier](https://github.com/martindrapeau/csvjson-json_beautifier) to beautify and format your JSON. [npm package here](https://www.npmjs.com/package/csvjson-json_beautifier). 232 | 233 | [JSON2_mod](https://github.com/martindrapeau/json2-mod) a replacement of `JSON` with more options to format your JSON. [npm package here](https://www.npmjs.com/package/json2-mod). -------------------------------------------------------------------------------- /json2csv.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * 4 | * CSVJSON.json2csv(data, options) 5 | * 6 | * Converts JSON to CSV 7 | * 8 | * Available options: 9 | * - separator: Character which acts as separator. For CSV use a comma (,). 10 | * For TSV use a tab (\t). 11 | * - flatten: Boolean indicating whether to flatten nested arrays or not. 12 | * Optional. Default false. 13 | * - output_csvjson_variant: Boolean indicating whether to output objects and 14 | * arrays as is as per the CSVJSON format variant. Default is false. 15 | * 16 | * Copyright (c) 2018-2019 Martin Drapeau 17 | * 18 | */ 19 | 20 | var errorMissingSeparator = 'Missing separator option.', 21 | errorEmpty = 'JSON is empty.', 22 | errorEmptyHeader = 'Could not detect header. Ensure first row cotains your column headers.', 23 | errorNotAnArray = 'Your JSON must be an array or an object.', 24 | errorItemNotAnObject = 'Item in array is not an object: {0}'; 25 | 26 | function flattenArray(array, ancestors) { 27 | ancestors || (ancestors = []); 28 | 29 | function combineKeys(a, b) { 30 | var result = a.slice(0); 31 | if (!Array.isArray(b)) return result; 32 | for (var i = 0; i < b.length; i++) 33 | if (result.indexOf(b[i]) === -1) result.push(b[i]); 34 | return result; 35 | } 36 | 37 | function extend(target, source) { 38 | target = target || {}; 39 | for (var prop in source) { 40 | if (typeof source[prop] === 'object') { 41 | target[prop] = extend(target[prop], source[prop]); 42 | } else { 43 | target[prop] = source[prop]; 44 | } 45 | } 46 | return target; 47 | } 48 | 49 | var rows = []; 50 | for (var i = 0; i < array.length; i++) { 51 | var o = array[i], 52 | row = {}, 53 | orows = {}, 54 | count = 1; 55 | 56 | if (o !== undefined && o !== null && (!isObject(o) || Array.isArray(o))) 57 | throw errorItemNotAnObject.replace('{0}', JSON.stringify(o)); 58 | 59 | var keys = getKeys(o); 60 | for (var k = 0; k < keys.length; k++) { 61 | var value = o[keys[k]], 62 | keyChain = combineKeys(ancestors, [keys[k]]), 63 | key = keyChain.join('.'); 64 | if (Array.isArray(value)) { 65 | orows[key] = flattenArray(value, keyChain); 66 | count += orows[key].length; 67 | } else { 68 | row[key] = value; 69 | } 70 | } 71 | 72 | if (count == 1) { 73 | rows.push(row); 74 | } else { 75 | var keys = getKeys(orows); 76 | for (var k = 0; k < keys.length; k++) { 77 | var key = keys[k]; 78 | for (var r = 0; r < orows[key].length; r++) { 79 | rows.push(extend(extend({}, row), orows[key][r])); 80 | } 81 | } 82 | } 83 | } 84 | return rows; 85 | } 86 | 87 | function isObject(o) { 88 | return o && typeof o == 'object'; 89 | } 90 | 91 | function getKeys(o) { 92 | if (!isObject(o)) return []; 93 | return Object.keys(o); 94 | } 95 | 96 | function convert(data, options) { 97 | options || (options = {}); 98 | 99 | if (!isObject(data)) throw errorNotAnArray; 100 | if (!Array.isArray(data)) data = [data]; 101 | 102 | var separator = options.separator || ','; 103 | if (!separator) throw errorMissingSeparator; 104 | 105 | var flatten = options.flatten || false; 106 | if (flatten) data = flattenArray(data); 107 | 108 | var allKeys = [], 109 | allRows = []; 110 | for (var i = 0; i < data.length; i++) { 111 | var o = data[i], 112 | row = {}; 113 | if (o !== undefined && o !== null && (!isObject(o) || Array.isArray(o))) 114 | throw errorItemNotAnObject.replace('{0}', JSON.stringify(o)); 115 | var keys = getKeys(o); 116 | for (var k = 0; k < keys.length; k++) { 117 | var key = keys[k]; 118 | if (allKeys.indexOf(key) === -1) allKeys.push(key); 119 | var value = o[key]; 120 | if (value === undefined && value === null) continue; 121 | if (typeof value == 'string') { 122 | row[key] = '"' + value.replace(/"/g, options.output_csvjson_variant ? '\\"' : '""') + '"'; 123 | if (options.output_csvjson_variant) row[key] = row[key].replace(/\n/g, '\\n'); 124 | } else { 125 | row[key] = JSON.stringify(value); 126 | if (!options.output_csvjson_variant && (isObject(value) || Array.isArray(value))) 127 | row[key] = '"' + row[key].replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"'; 128 | } 129 | } 130 | allRows.push(row); 131 | } 132 | 133 | keyValues = []; 134 | for (var i = 0; i < allKeys.length; i++) { 135 | keyValues.push('"' + allKeys[i].replace(/"/g, options.output_csvjson_variant ? '\\"' : '""') + '"'); 136 | } 137 | 138 | var csv = keyValues.join(separator)+'\n'; 139 | for (var r = 0; r < allRows.length; r++) { 140 | var row = allRows[r], 141 | rowArray = []; 142 | for (var k = 0; k < allKeys.length; k++) { 143 | var key = allKeys[k]; 144 | rowArray.push(row[key] || (options.output_csvjson_variant ? 'null' : '')); 145 | } 146 | csv += rowArray.join(separator) + (r < allRows.length-1 ? '\n' : ''); 147 | } 148 | 149 | return csv; 150 | } 151 | 152 | // CommonJS or Browser 153 | if (typeof exports !== 'undefined') { 154 | if (typeof module !== 'undefined' && module.exports) { 155 | exports = module.exports = convert; 156 | } 157 | exports.json2csv = convert; 158 | } else { 159 | this.CSVJSON || (this.CSVJSON = {}); 160 | this.CSVJSON.json2csv = convert; 161 | } 162 | 163 | }).call(this); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "csvjson-json2csv", 3 | "description": "Converts JSON to CSV. Powers the most used online tool CSVJSON https://www.csvjson.com/json2csv. Used by thousands everyday.", 4 | "version": "1.0.3", 5 | "author": "Martin Drapeau ", 6 | "contributors": [], 7 | "dependencies": { }, 8 | "devDependencies": { }, 9 | "main": "json2csv.js", 10 | "keywords": ["csv", "json", "parse", "convert"], 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/martindrapeau/csvjson-json2csv.git" 14 | }, 15 | "license": "MIT" 16 | } -------------------------------------------------------------------------------- /test-browser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CSVJSON's json2csv Test 5 | 6 | 7 |

CSVJSON's json2csv Test

8 | 9 |

Test

10 |
const obj = [
 11 |   {
 12 |     "album": "The White Stripes",
 13 |     "year": 1999,
 14 |     "US_peak_chart_post": "-"
 15 |   },
 16 |   {
 17 |     "album": "De Stijl",
 18 |     "year": 2000,
 19 |     "US_peak_chart_post": "-"
 20 |   },
 21 |   {
 22 |     "album": "White Blood Cells",
 23 |     "year": 2001,
 24 |     "US_peak_chart_post": 61
 25 |   },
 26 |   {
 27 |     "album": "Elephant",
 28 |     "year": 2003,
 29 |     "US_peak_chart_post": 6
 30 |   },
 31 |   {
 32 |     "album": "Get Behind Me Satan",
 33 |     "year": 2005,
 34 |     "US_peak_chart_post": 3
 35 |   },
 36 |   {
 37 |     "album": "Icky Thump",
 38 |     "year": 2007,
 39 |     "US_peak_chart_post": 2
 40 |   },
 41 |   {
 42 |     "album": "Under Great White Northern Lights",
 43 |     "year": 2010,
 44 |     "US_peak_chart_post": 11
 45 |   },
 46 |   {
 47 |     "album": "Live in Mississippi",
 48 |     "year": 2011,
 49 |     "US_peak_chart_post": "-"
 50 |   },
 51 |   {
 52 |     "album": "Live at the Gold Dollar",
 53 |     "year": 2012,
 54 |     "US_peak_chart_post": "-"
 55 |   },
 56 |   {
 57 |     "album": "Nine Miles from the White City",
 58 |     "year": 2013,
 59 |     "US_peak_chart_post": "-"
 60 |   }
 61 | ];
 62 | 
 63 | const csv = CSVJSON.json2csv(obj);
64 | 65 |

Expected

66 |
"album","year","US_peak_chart_post"
 67 | "The White Stripes",1999,"-"
 68 | "De Stijl",2000,"-"
 69 | "White Blood Cells",2001,61
 70 | "Elephant",2003,6
 71 | "Get Behind Me Satan",2005,3
 72 | "Icky Thump",2007,2
 73 | "Under Great White Northern Lights",2010,11
 74 | "Live in Mississippi",2011,"-"
 75 | "Live at the Gold Dollar",2012,"-"
 76 | "Nine Miles from the White City",2013,"-"
77 | 78 |

Execution

79 |

 80 | 
 81 |   

Test Result

82 |

83 | 84 | 85 | 153 | 154 | -------------------------------------------------------------------------------- /test-node.js: -------------------------------------------------------------------------------- 1 | const json2csv = require('./json2csv.js'); 2 | const obj = [ 3 | { 4 | "album": "The White Stripes", 5 | "year": 1999, 6 | "US_peak_chart_post": "-" 7 | }, 8 | { 9 | "album": "De Stijl", 10 | "year": 2000, 11 | "US_peak_chart_post": "-" 12 | }, 13 | { 14 | "album": "White Blood Cells", 15 | "year": 2001, 16 | "US_peak_chart_post": 61 17 | }, 18 | { 19 | "album": "Elephant", 20 | "year": 2003, 21 | "US_peak_chart_post": 6 22 | }, 23 | { 24 | "album": "Get Behind Me Satan", 25 | "year": 2005, 26 | "US_peak_chart_post": 3 27 | }, 28 | { 29 | "album": "Icky Thump", 30 | "year": 2007, 31 | "US_peak_chart_post": 2 32 | }, 33 | { 34 | "album": "Under Great White Northern Lights", 35 | "year": 2010, 36 | "US_peak_chart_post": 11 37 | }, 38 | { 39 | "album": "Live in Mississippi", 40 | "year": 2011, 41 | "US_peak_chart_post": "-" 42 | }, 43 | { 44 | "album": "Live at the Gold Dollar", 45 | "year": 2012, 46 | "US_peak_chart_post": "-" 47 | }, 48 | { 49 | "album": "Nine Miles from the White City", 50 | "year": 2013, 51 | "US_peak_chart_post": "-" 52 | } 53 | ]; 54 | const csv = json2csv(obj); 55 | const expected = `"album","year","US_peak_chart_post" 56 | "The White Stripes",1999,"-" 57 | "De Stijl",2000,"-" 58 | "White Blood Cells",2001,61 59 | "Elephant",2003,6 60 | "Get Behind Me Satan",2005,3 61 | "Icky Thump",2007,2 62 | "Under Great White Northern Lights",2010,11 63 | "Live in Mississippi",2011,"-" 64 | "Live at the Gold Dollar",2012,"-" 65 | "Nine Miles from the White City",2013,"-"`; 66 | console.log('==Expected=='); 67 | console.log(expected); 68 | console.log('==Execution=='); 69 | console.log(csv); 70 | console.log('==Result=='); 71 | if (csv === expected) { 72 | console.log('Success!'); 73 | } else { 74 | console.log('Failed.'); 75 | } 76 | --------------------------------------------------------------------------------