diff --git a/README.md b/README.md index 9dbc315..74f66b3 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,16 @@ > 让excel支持表达复杂的json格式,将xlsx文件转成json。 ### 日志 -* 2017-10-31 v0.3.1 - * 修复: 对象类型列,对象值里面有冒号值解析错误。 - * 功能: 增加#id类型,主键索引(json格式以map形式输出,无id的表以数组格式输出)。 - * 功能: 输出的json是否压缩可以在config.json里面配置。 +* 2017-10-31 v0.3.2 + * sheet名字以`#`开头则不导出此表。 + * 增加输出d.ts功能。 + * 去掉config.json中的arraySeparator配置。 -* 2017-10-26 v0.3.0 - * 修复中文自动追加拼音问题。 - * 修复日期解析错误。 - * 去掉主外键功能(以后再追加)。 - * 增加代码静态检查,核心代码重写并改成ES6语法。 - * 更新依赖插件的版本。 +### 分支 -### npm相关 -* 如需当做npm模块引用请切换到`npm`分支。 +* `master`为主分支,此分支用于发布版本,包含当前稳定代码,不要往主分支直接提交代码。 +* `dev`为开发分支,新功能提交到此分支,待稳定后合并到`master`分支。 +* 如需当做npm模块引用请切换到`npm`分支(尚有功能未合并)。 ### 使用说明 * 目前只支持.xlsx格式,不支持.xls格式。 @@ -48,15 +44,11 @@ npm install /** * 导出的json存放的位置 */ - "dest": "./json", - - /** - * 数组的分隔符 - * 有时候特殊需要,在excel单元格中里面逗号被当做他用。 - * 已过时,将在v0.5.x移除。参考列类型是数组类型时候表头设置。 - */ - "arraySeparator":"," + "dest": "./json" }, + + "ts":false,//是否导出d.ts(for typescript) + "json": { /** * 导出的json是否需要压缩 @@ -116,6 +108,7 @@ npm install ``` ### 支持以下数据类型 + * number 数字类型。 * boolean 布尔。 * string 字符串。 @@ -128,6 +121,7 @@ npm install * id 主键类型(当表中有这个类型的时候,json会以map格式输出,否则以数组格式输出)。 ### 表头规则 + * 基本数据类型(string,number,bool)时候,一般不需要设置会自动判断,但是也可以明确声明数据类型。 * 字符串类型:命名形式 `列名#string` 。 * 数字类型:命名形式 `列名#number` 。 @@ -136,25 +130,24 @@ npm install * 基本类型数组:命名形式 `列名#[]`,数组元素默认用逗号分隔(a,b,c),自定义数组元素分隔符`列名#[]/`(a/b/c)。 * 对象:命名形式 `列名#{}` 。 * 对象数组:命名形式`列名#[{}]` 。 -* 主键:命名形式`列名#id` 。 +* 主键:命名形式`列名#id` ,设置此将会输出为json map 格式。 +### 规则 -### 数据规则 * 关键符号都是半角符号。 * 对象属性使用分号`;`分割。 +* sheet名字以`#`开头则不导出此表。 ### TODO + - [ ] 列为数组类型时候,嵌套复杂类型。 - [ ] 列为对象类型时候,嵌套复杂类型。 - [ ] 外键支持。 - [ ] 将主分支的代码合并到npm分支。 -- [x] 数组分隔符的设置放到表头,默认用逗号。 - -### 感谢 -某些想法也是借鉴了一个clojure的excel转json的开源项目 [excel-to-json](https://github.com/mhaemmerle/excel-to-json)。 ### 补充 -* windows/mac/linux都支持。 + +* 可在windows/mac/linux下运行。 * 项目地址 [xlsx2json master](https://github.com/koalaylj/xlsx2json) * 如有问题可以到QQ群内讨论:223460081 * 招募协作开发者,有时间帮助一起维护下这个项目,可以发issue或者到qq群里把你github邮箱告诉我。 diff --git a/config.json b/config.json index d643e4f..8d5ba1d 100644 --- a/config.json +++ b/config.json @@ -2,10 +2,10 @@ "xlsx": { "head": 2, "src": "./excel/**/[^~$]*.xlsx", - "dest": "./json", - "arraySeparator": "," + "dest": "./json" }, + "ts": false, "json": { "uglify": false } -} +} \ No newline at end of file diff --git a/excel/cat_config.xlsx b/excel/cat_config.xlsx new file mode 100644 index 0000000..5ed1022 Binary files /dev/null and b/excel/cat_config.xlsx differ diff --git a/excel/product.xlsx b/excel/product.xlsx index ba0c33c..faf9c99 100644 Binary files a/excel/product.xlsx and b/excel/product.xlsx differ diff --git a/index.js b/index.js index a6f68ba..68cbeef 100644 --- a/index.js +++ b/index.js @@ -32,7 +32,7 @@ let keys = Object.keys(commands); for (let key in commands) { let alias_array = commands[key].alias; - alias_array.forEach((e) => { + alias_array.forEach(e => { alias_map[e] = key; }); } @@ -41,7 +41,7 @@ parsed_cmds = parseCommandLine(process.argv); // console.log("%j", parsed_cmds); -parsed_cmds.forEach(function(e) { +parsed_cmds.forEach(function (e) { exec(e); }); @@ -53,21 +53,21 @@ parsed_cmds.forEach(function(e) { function exportJson(args) { if (typeof args === 'undefined' || args.length === 0) { - glob(config.xlsx.src, function(err, files) { + glob(config.xlsx.src, function (err, files) { if (err) { console.error("exportJson error:", err); throw err; } - files.forEach(function(element, index, array) { - xlsx.toJson(path.join(__dirname, element), path.join(__dirname, config.xlsx.dest),config); + files.forEach(function (element, index, array) { + xlsx.toJson(path.join(__dirname, element), path.join(__dirname, config.xlsx.dest), config); }); }); } else { if (args instanceof Array) { - args.forEach(function(element, index, array) { - xlsx.toJson(path.join(__dirname, element), path.join(__dirname, config.xlsx.dest),config); + args.forEach(function (element, index, array) { + xlsx.toJson(path.join(__dirname, element), path.join(__dirname, config.xlsx.dest), config); }); } } @@ -120,7 +120,7 @@ function parseCommandLine(args) { let pos = 0; let cmd; - cli.forEach(function(element, index, array) { + cli.forEach(function (element, index, array) { //replace alias name with real name. if (element.indexOf('--') === -1 && element.indexOf('-') === 0) { @@ -170,4 +170,4 @@ function defaultCommand() { } } -/*************************************************************************/ +/*************************************************************************/ \ No newline at end of file diff --git a/lib/xlsx-to-json.js b/lib/xlsx-to-json.js index e5fb8e5..3aceee0 100755 --- a/lib/xlsx-to-json.js +++ b/lib/xlsx-to-json.js @@ -1,9 +1,25 @@ const xlsx = require('node-xlsx'); const fs = require('fs'); const path = require('path'); -// const moment = require('moment'); -let arraySeparator; +let _config; + +// class StringBuffer { +// constructor(str) { +// this._str_ = []; +// if (str) { +// this.append(str); +// } +// } + +// toString() { +// return this._str_.join(""); +// } + +// append(str) { +// this._str_.push(str); +// } +// } /** * sheet(table) 的类型 @@ -45,42 +61,15 @@ const DataType = { UNKOWN: 'unkown' }; -module.exports = { - - /** - * convert xlsx file to json and save it to file system. - * @param {String} src path of .xlsx files. - * @param {String} dest directory for exported json files. - * @param {Number} headIndex index of head line. - * @param {String} separator array separator. - * - * excel structure - * workbook > worksheet > table(row column) - */ - toJson: function(src, dest, config) { - - let headIndex = config.xlsx.head - 1; - arraySeparator = config.xlsx.arraySeparator; - let uglifyJson = config.json.uglify; - - if (!fs.existsSync(dest)) { - fs.mkdirSync(dest); - } - - console.log("parsing excel:", src); - - let workbook = xlsx.parse(src); - parseWorkbook(workbook, dest, headIndex, uglifyJson); - } -}; - /** * convert worksheet in workbook and save to file for each. * @param {[Object]} workbook json object of excel's workbook. * @param {[String]} dest directory for exported json files. * @param {[Number]} headIndex index of head line. */ -function parseWorkbook(workbook, dest, headIndex, uglifyJson) { +function parseWorkbook(workbook, dest, headIndex, excelName) { + + let dtsstring = ""; workbook.forEach(sheet => { @@ -89,16 +78,93 @@ function parseWorkbook(workbook, dest, headIndex, uglifyJson) { let parsedSheet = parseSheet(sheet, headIndex); let dest_file = path.resolve(dest, sheet.name + ".json"); - let formatedJson = JSON.stringify(parsedSheet, null, uglifyJson ? 0 : 2); //, null, 2 - fs.writeFile(dest_file, formatedJson, err => { + let resultJson = JSON.stringify(parsedSheet.result, null, _config.json.uglify ? 0 : 2); //, null, 2 + fs.writeFile(dest_file, resultJson, err => { if (err) { console.error("error:", err); throw err; } - console.log('exported successfully --> ', path.basename(dest_file)); + console.log('exported json successfully --> ', path.basename(dest_file)); }); + + if (_config.ts) { + dtsstring += formatDTS(sheet.name, parsedSheet.head); + } } }); + + if (_config.ts) { + let dest_file_dts = path.resolve(dest, excelName + ".d.ts"); + fs.writeFile(dest_file_dts, dtsstring, err => { + if (err) { + console.error("error:", err); + throw err; + } + console.log('exported t.ds successfully --> ', path.basename(dest_file_dts)); + }); + } +} + +/** + * + * @param {String} name the excel file name will be use on create d.ts + * @param {Object} head the excel head will be the javescript field + */ +function formatDTS(name, head) { + let className = name.substring(0, 1).toUpperCase() + name.substring(1); + let strHead = "interface " + className + "Template {\r\n"; + for (let i = 0; i < head.names.length; ++i) { + let typesDes = "any"; + switch (head.types[i]) { + case DataType.NUMBER: + { + typesDes = "number"; + break; + } + case DataType.STRING: + { + typesDes = "string"; + break; + } + case DataType.BOOL: + { + typesDes = "boolean"; + break; + } + case DataType.ID: + { + typesDes = "string"; + break; + } + case DataType.ARRAY: + { + typesDes = "any[]"; + break; + } + case DataType.OBJECT: + { + typesDes = "any"; + break; + } + case DataType.OBJECT_ARRAY: + { + typesDes = "any[]"; + break; + } + case DataType.UNKOWN: + { + typesDes = "any"; + break; + } + default: + { + typesDes = "any"; + } + } + strHead += "\t" + head.names[i] + ": " + typesDes + "\r\n"; + } + strHead += "}\r\n"; + return strHead; } /** @@ -110,12 +176,14 @@ function parseWorkbook(workbook, dest, headIndex, uglifyJson) { */ function parseSheet(sheet, headIndex) { - console.log('\t parsing sheet', sheet.name); + console.log(' * sheet:', sheet.name); if (sheet && sheet.data) { let head = parseHead(sheet, headIndex); + // console.log('\t parsing head', JSON.stringify(head)); + let result; if (head.sheetType === SheetType.NORMAL) { @@ -129,7 +197,7 @@ function parseSheet(sheet, headIndex) { let row = sheet.data[i_row]; let parsedRow = parseRow(row, i_row, head); - if (head.sheetType === SheetType.NORMAL) { ////json以数组的格式输出 + if (head.sheetType === SheetType.NORMAL) { // json以数组的格式输出 result.push(parsedRow); } else if (head.sheetType === SheetType.PRIMARY) { //json以map的格式输出 let id = parsedRow[head.getIdKey()]; @@ -138,7 +206,10 @@ function parseSheet(sheet, headIndex) { throw '无法识别表格类型!'; } } - return result; + return { + result, + head + }; } } @@ -161,7 +232,7 @@ function parseHead(sheet, headIndex) { //表类型 普通表 主表 引用表 sheetType: SheetType.NORMAL, - getIdKey: function() { + getIdKey: function () { let id_col_index = this.types.indexOf(DataType.ID); if (id_col_index < 0) { throw '获取不到id列的名字'; @@ -195,15 +266,19 @@ function parseRow(row, rowIndex, head) { let result = {}; let id; - console.log('parsing row', row); + // console.log('parsing row', row); - row.forEach((cell, index) => { - - // if (cell) { + for (let index = 0; index < row.length; index++) { + let cell = row[index]; let name = head.names[index]; let type = head.types[index]; + if (cell === null || cell === undefined) { + result[name] = null; + continue; + } + switch (type) { case DataType.ID: // number string boolean if (isNumber(cell)) { @@ -219,9 +294,7 @@ function parseRow(row, rowIndex, head) { } else if (isBoolean(cell)) { result[name] = toBoolean(cell); } else { - if (cell) { - result[name] = cell; - } + result[name] = cell; } break; case DataType.DATE: @@ -229,9 +302,7 @@ function parseRow(row, rowIndex, head) { //xlsx's bug!!! result[name] = numdate(cell); } else { - if (cell) { - result[name] = cell.toString(); - } + result[name] = cell.toString(); } break; case DataType.STRING: @@ -254,7 +325,7 @@ function parseRow(row, rowIndex, head) { } break; case DataType.ARRAY: //[number] [boolean] [string] todo:support [date] type - result[name] = parseBasicArrayField(cell, arraySeparator); + result[name] = parseBasicArrayField(cell, ','); break; case DataType.OBJECT_ARRAY: result[name] = parseObjectArrayField(cell); @@ -262,32 +333,16 @@ function parseRow(row, rowIndex, head) { default: // foo#[]| 处理自定义数组分隔符 if (type.indexOf(DataType.ARRAY) !== -1) { - // if (!type.endsWith(DataType.ARRAY)) { let separator = type.substr(-1, 1); //get the last character result[name] = parseBasicArrayField(cell, separator); - // } } else { - console.log('unrecognized type', '[' + rowIndex + ',' + index + ']', cell, typeof(cell)); + console.log('unrecognized type', '[' + rowIndex + ',' + index + ']', cell, typeof (cell)); } break; } - // } - }); + } return result; - - // switch (head.sheetType) { - // case SheetType.NORMAL: //json以数组的格式输出 - // return result; - // case SheetType.PRIMARY: //json以map的格式输出 - // let map = {}; - // map[id] = result; - // return map; - // default: - // throw '无法识别表格类型!'; - // } - - // return result; } /** @@ -305,15 +360,9 @@ function parseObjectArrayField(value) { } } - // if (typeof(value) === 'string' && value.indexOf(',') !== -1) { - // obj_array = value.split(','); - // } else { - // obj_array.push(value.toString()); - // }; - let result = []; - obj_array.forEach(function(e) { + obj_array.forEach(function (e) { if (e) { result.push(array2object(e.split(';'))); } @@ -329,7 +378,7 @@ function parseObjectArrayField(value) { */ function array2object(array) { let result = {}; - array.forEach(function(e) { + array.forEach(function (e) { if (e) { let colonIndex = e.indexOf(':'); let key = e.substring(0, colonIndex); @@ -360,11 +409,11 @@ function parseBasicArrayField(array, arraySeparator) { let result = []; if (isNumberArray(basic_array)) { - basic_array.forEach(function(element) { + basic_array.forEach(function (element) { result.push(Number(element)); }); } else if (isBooleanArray(basic_array)) { - basic_array.forEach(function(element) { + basic_array.forEach(function (element) { result.push(toBoolean(element)); }); } else { //string array @@ -386,7 +435,7 @@ function toBoolean(value) { * is a boolean array. */ function isBooleanArray(arr) { - return arr.every(function(element, index, array) { + return arr.every(function (element, index, array) { return isBoolean(element); }); } @@ -395,7 +444,7 @@ function isBooleanArray(arr) { * is a number array. */ function isNumberArray(arr) { - return arr.every(function(element, index, array) { + return arr.every(function (element, index, array) { return isNumber(element); }); } @@ -421,7 +470,7 @@ function isNumber(value) { */ function isBoolean(value) { - if (typeof(value) === "undefined") { + if (typeof (value) === "undefined") { return false; } @@ -463,3 +512,36 @@ function numdate(v) { return out; } //fuck over + + +module.exports = { + + /** + * convert xlsx file to json and save it to file system. + * @param {String} src path of .xlsx files. + * @param {String} dest directory for exported json files. + * @param {Number} headIndex index of head line. + * @param {String} separator array separator. + * + * excel structure + * workbook > worksheet > table(row column) + */ + toJson: function (src, dest, config) { + + _config = config; + + let headIndex = config.xlsx.head - 1; + + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest); + } + + let parsed_src = path.parse(src); + + let workbook = xlsx.parse(src); + + console.log("parsing excel:", parsed_src.base); + + parseWorkbook(workbook, dest, headIndex, path.join(dest, parsed_src.name)); + } +}; \ No newline at end of file