Skip to content

Commit

Permalink
Version 2.0: add access to any xlsx sheet, omit empty excel cells
Browse files Browse the repository at this point in the history
Extended a PR to provide access to sheets other than 1 through an options
object to allow future additions without breaking existing users.

Added ability to omit empty excel files from json output as requested in an
issue.
  • Loading branch information
stevetarver committed Feb 12, 2017
1 parent 0573755 commit 38fc768
Show file tree
Hide file tree
Showing 9 changed files with 430 additions and 167 deletions.
66 changes: 47 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,82 @@

# Convert Excel Files to JSON

## Install
## What

Parse Excel xlsx files into a list of javascript objects and optionally write that list as a JSON encoded file.

You may organize Excel data by columns or rows where the first column or row contains object key names and the remaining columns/rows contain object values.

Expected use is offline translation of Excel data to JSON files, although
async facilities are provided.
all methods are exported for other uses.

## Install

```$ npm install excel-as-json --save-dev```

## Use

```js
convertExcel = require('excel-as-json').processFile;
convertExcel(<src>, <sheet>, <dst>, isColOriented, callback);
convertExcel(src, dst, options, callback);
```

* src: path to source Excel file (xlsx only)
* sheet: the number of the sheet to process. The first sheet is 1. (Default: 1)
* dst: path to destination JSON file. If null, simply return the parsed object tree
* isColOriented: is an Excel row an object, or is a column an object (Default: false)
* options: an object containing
* sheet: 1 based sheet index as text - default '1'
* isColOriented: are object values in columns with keys in column A - default false
* omitEmptyFields: omit empty Excel fields from JSON output - default false
* callback(err, data): callback for completion notification

**NOTE** If options are not specified, defaults are used.

With these arguments, you can:

* convertExcel(src, sheet, dst)
will write a row oriented xlsx to file with no notification
* convertExcel(src, sheet, dst, true)
will write a col oriented xlsx to file with no notification
* convertExcel(src, sheet, dst, true, callback)
will write a col oriented xlsx to file and notify with errors and data
* convertExcel(src, null, null, true, callback)
will return errors and the parsed object tree in the callback
* convertExcel(src, dst) <br/>
will write a row oriented xlsx sheet 1 to `dst` as JSON with no notification
* convertExcel(src, dst, {isColOriented: true}) <br/>
will write a col oriented xlsx sheet 1 to file with no notification
* convertExcel(src, dst, {isColOriented: true}, callback) <br/>
will write a col oriented xlsx to file and notify with errors and parsed data
* convertExcel(src, null, null, callback) <br/>
will parse a row oriented xslx using default options and return errors and the parsed data in the callback

Convert a row/col oriented Excel file to JSON as a development task and
log errors:

```CoffeeScript
convertExcel = require('excel-as-json').processFile

convertExcel 'row.xlsx', 1, 'row.json', false, (err, data) ->
options =
sheet:'1'
isColOriented: false
omitEmtpyFields: false

convertExcel 'row.xlsx', 'row.json', options, (err, data) ->
if err then console.log "JSON conversion failure: #{err}"
convertExcel 'col.xlsx', 1, 'col.json', true, (err, data) ->

options =
sheet:'1'
isColOriented: true
omitEmtpyFields: false

convertExcel 'col.xlsx', 'col.json', options, (err, data) ->
if err then console.log "JSON conversion failure: #{err}"
```

Convert Excel file to an object tree and use that tree. Note that
properly formatted data will convert to the same object tree whether
row or column oriented.

```CoffeeScript
convertExcel = require('excel-as-json').processFile

convertExcel 'row.xlsx', 1, undefined, false, (err, data) ->
convertExcel 'row.xlsx', undefined, undefined, (err, data) ->
if err throw err
doSomethingInteresting data
convertExcel 'col.xlsx', 1, undefined, true, (err, data) ->

convertExcel 'col.xlsx', undefined, {isColOriented: true}, (err, data) ->
if err throw err
doSomethingInteresting data
```
Expand Down Expand Up @@ -296,10 +319,15 @@ excel dependency - although questionable, they appear to be benign.

## Change History

### 2.0.0
- **Breaking changes to most function signatures**
- Replace single option `isColOriented` with an options object to try to stabilize the processFile signature allowing future non-breaking feature additions.
- Add `sheet` option to specify a 1-based index into the Excel sheet collection - all of your data in a single Excel workbook.
- Add `omitEmptyFields` option that removes an object key-value if the corresponding Excel cell is empty.


### 1.0.0
- Changed process() to processFile() to avoid name collision with node's process object
- Automatically convert text numbers and booleans to native values
- Create destination directory if it does not exist

### 2.0.0
- Changed processFile() to accept the sheet number as the second argument (breaking API change).
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "excel-as-json",
"version": "1.0.4",
"version": "2.0.0",
"description": "Convert Excel data to JSON",
"author": "Steve Tarver <[email protected]>",
"license": "MIT",
Expand Down
3 changes: 2 additions & 1 deletion spec/all-specs.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ require './assignSpec'
require './convertSpec'
require './convertValueSpec'
require './parseKeyNameSpec'
require './processFileSpec'
require './transposeSpec'
require './validateOptionsSpec'
require './processFileSpec'
80 changes: 62 additions & 18 deletions spec/assignSpec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -5,91 +5,135 @@ chai = require 'chai'
chai.should()
expect = chai.expect;

# NOTE: the excel package uses '' for all empty cells
EMPTY_CELL = ''
DEFAULT_OPTIONS =
omitEmptyFields: false


describe 'assign', ->

it 'should assign first level properties', ->
subject = {}
assign subject, 'foo', 'clyde'
assign subject, 'foo', 'clyde', DEFAULT_OPTIONS
subject.foo.should.equal 'clyde'


it 'should assign second level properties', ->
subject = {}
assign subject, 'foo.bar', 'wombat'
assign subject, 'foo.bar', 'wombat', DEFAULT_OPTIONS
subject.foo.bar.should.equal 'wombat'


it 'should assign third level properties', ->
subject = {}
assign subject, 'foo.bar.bazz', 'honey badger'
assign subject, 'foo.bar.bazz', 'honey badger', DEFAULT_OPTIONS
subject.foo.bar.bazz.should.equal 'honey badger'


it 'should convert text to numbers', ->
subject = {}
assign subject, 'foo.bar.bazz', '42'
assign subject, 'foo.bar.bazz', '42', DEFAULT_OPTIONS
subject.foo.bar.bazz.should.equal 42


it 'should convert text to booleans', ->
subject = {}
assign subject, 'foo.bar.bazz', 'true'
assign subject, 'foo.bar.bazz', 'true', DEFAULT_OPTIONS
subject.foo.bar.bazz.should.equal true
assign subject, 'foo.bar.bazz', 'false'
assign subject, 'foo.bar.bazz', 'false', DEFAULT_OPTIONS
subject.foo.bar.bazz.should.equal false


it 'should overwrite existing values', ->
subject = {}
assign subject, 'foo.bar.bazz', 'honey badger'
assign subject, 'foo.bar.bazz', 'honey badger', DEFAULT_OPTIONS
subject.foo.bar.bazz.should.equal 'honey badger'
assign subject, 'foo.bar.bazz', "don't care"
assign subject, 'foo.bar.bazz', "don't care", DEFAULT_OPTIONS
subject.foo.bar.bazz.should.equal "don't care"


it 'should assign properties to objects in a list', ->
subject = {}
assign subject, 'foo.bar[0].what', 'that'
assign subject, 'foo.bar[0].what', 'that', DEFAULT_OPTIONS
subject.foo.bar[0].what.should.equal 'that'


it 'should assign properties to objects in a list with first entry out of order', ->
subject = {}
assign subject, 'foo.bar[1].what', 'that'
assign subject, 'foo.bar[0].what', 'this'
assign subject, 'foo.bar[1].what', 'that', DEFAULT_OPTIONS
assign subject, 'foo.bar[0].what', 'this', DEFAULT_OPTIONS
subject.foo.bar[0].what.should.equal 'this'
subject.foo.bar[1].what.should.equal 'that'


it 'should assign properties to objects in a list with second entry out of order', ->
subject = {}
assign subject, 'foo.bar[0].what', 'this'
assign subject, 'foo.bar[2].what', 'that'
assign subject, 'foo.bar[1].what', 'other'
assign subject, 'foo.bar[0].what', 'this', DEFAULT_OPTIONS
assign subject, 'foo.bar[2].what', 'that', DEFAULT_OPTIONS
assign subject, 'foo.bar[1].what', 'other', DEFAULT_OPTIONS
subject.foo.bar[0].what.should.equal 'this'
subject.foo.bar[2].what.should.equal 'that'
subject.foo.bar[1].what.should.equal 'other'


it 'should split a semicolon delimited list for flat arrays', ->
subject = {}
assign subject, 'foo.bar[]', 'peter;paul;mary'
assign subject, 'foo.bar[]', 'peter;paul;mary', DEFAULT_OPTIONS
subject.foo.bar.toString().should.equal ['peter','paul','mary'].toString()


it 'should convert text in a semicolon delimited list to numbers', ->
subject = {}
assign subject, 'foo.bar[]', 'peter;-43;mary'
assign subject, 'foo.bar[]', 'peter;-43;mary', DEFAULT_OPTIONS
subject.foo.bar.toString().should.equal ['peter',-43,'mary'].toString()


it 'should convert text in a semicolon delimited list to booleans', ->
subject = {}
assign subject, 'foo.bar[]', 'peter;false;true'
assign subject, 'foo.bar[]', 'peter;false;true', DEFAULT_OPTIONS
subject.foo.bar.toString().should.equal ['peter',false,true].toString()


it 'should not split a semicolon list with a terminal indexed array', ->
subject = {}
assign subject, 'foo.bar[0]', 'peter;paul;mary'
assign subject, 'foo.bar[0]', 'peter;paul;mary', DEFAULT_OPTIONS
subject.foo.bar.should.equal 'peter;paul;mary'


it 'should omit empty scalar fields when directed', ->
o =
omitEmptyFields: true
subject = {}
assign subject, 'foo', EMPTY_CELL, o
subject.should.not.have.property 'foo'


it 'should omit empty nested scalar fields when directed', ->
o =
omitEmptyFields: true
subject = {}
assign subject, 'foo.bar', EMPTY_CELL, o
subject.should.have.property 'foo'
subject.foo.should.not.have.property 'bar'


it 'should omit nested array fields when directed', ->
o =
omitEmptyFields: true

# specified as an entire list
subject = {}
assign subject, 'foo[]', EMPTY_CELL, o
subject.should.not.have.property 'foo'

# specified as a list
subject = {}
assign subject, 'foo[0]', EMPTY_CELL, o
subject.should.not.have.property 'foo'

# specified as a list of objects
subject = {}
assign subject, 'foo[0].bar', 'bazz', o
assign subject, 'foo[1].bar', EMPTY_CELL, o
subject.foo[1].should.not.have.property 'bar'
Loading

0 comments on commit 38fc768

Please sign in to comment.