Skip to content

Commit

Permalink
version bump 0.6.20: more flexible JSON
Browse files Browse the repository at this point in the history
- sheet_to_json more flexible, sheet_to_row_object_array now just wraps it
o opts.header = 1 for array of arrays
o opts.header = 'A' for spreadsheet column labels
o custom opts.header array for custom labels
o opts.range = n starts from row n
o opts.range = range restricts writer to work within the specified range
- fixes SheetJS#42
  • Loading branch information
SheetJSDev committed May 27, 2014
1 parent 84f2a0c commit 2bd2d1e
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 41 deletions.
2 changes: 1 addition & 1 deletion bits/01_version.js
@@ -1 +1 @@
XLS.version = '0.6.19';
XLS.version = '0.6.20';
30 changes: 21 additions & 9 deletions bits/90_utils.js
Expand Up @@ -29,22 +29,31 @@ function format_cell(cell, v) {
try { return (cell.w = SSF.format(cell.XF.ifmt||0, v)); } catch(e) { return v; }
}

function sheet_to_row_object_array(sheet, opts){
var val, row, r, hdr = {}, isempty, R, C, v;
function sheet_to_json(sheet, opts){
var val, row, range, header, offset = 1, r, hdr = {}, isempty, R, C, v;
var out = [];
opts = opts || {};
if(!sheet || !sheet["!ref"]) return out;
r = decode_range(sheet["!ref"]);
range = opts.range || sheet["!ref"];
header = opts.header || "";
switch(typeof range) {
case 'string': r = decode_range(range); break;
case 'number': r = decode_range(sheet["!ref"]); r.s.r = range; break;
default: r = range;
}
if(header) offset = 0;
for(R=r.s.r, C = r.s.c; C <= r.e.c; ++C) {
val = sheet[encode_cell({c:C,r:R})];
if(!val) continue;
hdr[C] = format_cell(val);
if(header === "A") hdr[C] = encode_col(C);
else if(header === 1) hdr[C] = C;
else if(Array.isArray(header)) hdr[C] = header[C - r.s.c];
else if(!val) continue;
else hdr[C] = format_cell(val);
}

for (R = r.s.r + 1; R <= r.e.r; ++R) {
for (R = r.s.r + offset; R <= r.e.r; ++R) {
isempty = true;
/* row index available as __rowNum__ */
row = Object.create({ __rowNum__ : R });
row = header === 1 ? [] : Object.create({ __rowNum__ : R });
for (C = r.s.c; C <= r.e.c; ++C) {
val = sheet[encode_cell({c: C,r: R})];
if(!val || !val.t) continue;
Expand All @@ -65,6 +74,8 @@ function sheet_to_row_object_array(sheet, opts){
return out;
}

function sheet_to_row_object_array(sheet, opts) { if(!opts) opts = {}; delete opts.range; return sheet_to_json(sheet, opts); }

function sheet_to_csv(sheet, opts) {
var out = [], txt = "";
opts = opts || {};
Expand Down Expand Up @@ -114,8 +125,9 @@ var utils = {
decode_range: decode_range,
sheet_to_csv: sheet_to_csv,
make_csv: sheet_to_csv,
make_json: sheet_to_row_object_array,
make_json: sheet_to_json,
get_formulae: get_formulae,
format_cell: format_cell,
sheet_to_json: sheet_to_json,
sheet_to_row_object_array: sheet_to_row_object_array
};
4 changes: 2 additions & 2 deletions dist/xls.core.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/xls.core.min.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/xls.full.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/xls.full.min.map

Large diffs are not rendered by default.

32 changes: 22 additions & 10 deletions dist/xls.js
Expand Up @@ -3,7 +3,7 @@
/*jshint funcscope:true */
var XLS = {};
(function(XLS){
XLS.version = '0.6.19';
XLS.version = '0.6.20';
var current_codepage = 1252, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('codepage');
Expand Down Expand Up @@ -6430,22 +6430,31 @@ function format_cell(cell, v) {
try { return (cell.w = SSF.format(cell.XF.ifmt||0, v)); } catch(e) { return v; }
}

function sheet_to_row_object_array(sheet, opts){
var val, row, r, hdr = {}, isempty, R, C, v;
function sheet_to_json(sheet, opts){
var val, row, range, header, offset = 1, r, hdr = {}, isempty, R, C, v;
var out = [];
opts = opts || {};
if(!sheet || !sheet["!ref"]) return out;
r = decode_range(sheet["!ref"]);
range = opts.range || sheet["!ref"];
header = opts.header || "";
switch(typeof range) {
case 'string': r = decode_range(range); break;
case 'number': r = decode_range(sheet["!ref"]); r.s.r = range; break;
default: r = range;
}
if(header) offset = 0;
for(R=r.s.r, C = r.s.c; C <= r.e.c; ++C) {
val = sheet[encode_cell({c:C,r:R})];
if(!val) continue;
hdr[C] = format_cell(val);
if(header === "A") hdr[C] = encode_col(C);
else if(header === 1) hdr[C] = C;
else if(Array.isArray(header)) hdr[C] = header[C - r.s.c];
else if(!val) continue;
else hdr[C] = format_cell(val);
}

for (R = r.s.r + 1; R <= r.e.r; ++R) {
for (R = r.s.r + offset; R <= r.e.r; ++R) {
isempty = true;
/* row index available as __rowNum__ */
row = Object.create({ __rowNum__ : R });
row = header === 1 ? [] : Object.create({ __rowNum__ : R });
for (C = r.s.c; C <= r.e.c; ++C) {
val = sheet[encode_cell({c: C,r: R})];
if(!val || !val.t) continue;
Expand All @@ -6466,6 +6475,8 @@ function sheet_to_row_object_array(sheet, opts){
return out;
}

function sheet_to_row_object_array(sheet, opts) { if(!opts) opts = {}; delete opts.range; return sheet_to_json(sheet, opts); }

function sheet_to_csv(sheet, opts) {
var out = [], txt = "";
opts = opts || {};
Expand Down Expand Up @@ -6515,9 +6526,10 @@ var utils = {
decode_range: decode_range,
sheet_to_csv: sheet_to_csv,
make_csv: sheet_to_csv,
make_json: sheet_to_row_object_array,
make_json: sheet_to_json,
get_formulae: get_formulae,
format_cell: format_cell,
sheet_to_json: sheet_to_json,
sheet_to_row_object_array: sheet_to_row_object_array
};
XLS.parse_xlscfb = parse_xlscfb;
Expand Down
4 changes: 2 additions & 2 deletions dist/xls.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/xls.min.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "xlsjs",
"version": "0.6.19",
"version": "0.6.20",
"author": "sheetjs",
"description": "XLS (Excel 97-2004 Spreadsheet) and XML (2003/2004) parser",
"keywords": [ "xls", "office", "excel", "spreadsheet" ],
Expand Down
100 changes: 100 additions & 0 deletions test.js
Expand Up @@ -484,6 +484,106 @@ describe('invalid files', function() {
});
});
});
describe('json output', function() {
function datenum(v, date1904) {
if(date1904) v+=1462;
var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
var ws = {};
var range = {s: {c:10000000, r:10000000}, e: {c:0, r:0 }};
for(var R = 0; R != data.length; ++R) {
for(var C = 0; C != data[R].length; ++C) {
if(range.s.r > R) range.s.r = R;
if(range.s.c > C) range.s.c = C;
if(range.e.r < R) range.e.r = R;
if(range.e.c < C) range.e.c = C;
var cell = {v: data[R][C] };
if(cell.v == null) continue;
var cell_ref = X.utils.encode_cell({c:C,r:R});
if(typeof cell.v === 'number') cell.t = 'n';
else if(typeof cell.v === 'boolean') cell.t = 'b';
else if(cell.v instanceof Date) {
cell.t = 'n'; cell.z = X.SSF._table[14];
cell.v = datenum(cell.v);
}
else cell.t = 's';
ws[cell_ref] = cell;
}
}
if(range.s.c < 10000000) ws['!ref'] = X.utils.encode_range(range);
return ws;
}
function seeker(json, keys, val) {
for(var i = 0; i != json.length; ++i) {
for(var j = 0; j != keys.length; ++j) {
if(json[i][keys[j]] === val) throw new Error("found " + val + " in row " + i + " key " + keys[j]);
}
}
}
var data, ws;
before(function() {
data = [
[1,2,3],
[true, false, null, "sheetjs"],
["foo","bar",new Date("2014-02-19T14:30Z"), "0.3"],
["baz", null, "qux"]
];
ws = sheet_from_array_of_arrays(data);
});
it('should use first-row headers and full sheet by default', function() {
var json = X.utils.sheet_to_json(ws);
assert.equal(json.length, data.length - 1);
assert.equal(json[0][1], true);
assert.equal(json[1][2], "bar");
assert.equal(json[2][3], "qux");
assert.doesNotThrow(function() { seeker(json, [1,2,3], "sheetjs"); });
assert.throws(function() { seeker(json, [1,2,3], "baz"); });
});
it('should create array of arrays if header == 1', function() {
var json = X.utils.sheet_to_json(ws, {header:1});
assert.equal(json.length, data.length);
assert.equal(json[1][0], true);
assert.equal(json[2][1], "bar");
assert.equal(json[3][2], "qux");
assert.doesNotThrow(function() { seeker(json, [0,1,2], "sheetjs"); });
assert.throws(function() { seeker(json, [0,1,2,3], "sheetjs"); });
assert.throws(function() { seeker(json, [0,1,2], "baz"); });
});
it('should use column names if header == "A"', function() {
var json = X.utils.sheet_to_json(ws, {header:'A'});
assert.equal(json.length, data.length);
assert.equal(json[1]['A'], true);
assert.equal(json[2]['B'], "bar");
assert.equal(json[3]['C'], "qux");
assert.doesNotThrow(function() { seeker(json, "ABC", "sheetjs"); });
assert.throws(function() { seeker(json, "ABCD", "sheetjs"); });
assert.throws(function() { seeker(json, "ABC", "baz"); });
});
it('should use column labels if specified', function() {
var json = X.utils.sheet_to_json(ws, {header:["O","D","I","N"]});
assert.equal(json.length, data.length);
assert.equal(json[1]['O'], true);
assert.equal(json[2]['D'], "bar");
assert.equal(json[3]['I'], "qux");
assert.doesNotThrow(function() { seeker(json, "ODI", "sheetjs"); });
assert.throws(function() { seeker(json, "ODIN", "sheetjs"); });
assert.throws(function() { seeker(json, "ODIN", "baz"); });
});
[["string", "A2:D4"], ["numeric", 1], ["object", {s:{r:1,c:0},e:{r:3,c:3}}]].forEach(function(w) {
it('should accept custom ' + w[0] + ' range', function() {
var json = X.utils.sheet_to_json(ws, {header:1, range:w[1]});
assert.equal(json.length, 3);
assert.equal(json[0][0], true);
assert.equal(json[1][1], "bar");
assert.equal(json[2][2], "qux");
assert.doesNotThrow(function() { seeker(json, [0,1,2], "sheetjs"); });
assert.throws(function() { seeker(json, [0,1,2,3], "sheetjs"); });
assert.throws(function() { seeker(json, [0,1,2], "baz"); });
});
});
});
describe('encryption', function() {
password_files.forEach(function(x) {
describe(x, function() {
Expand Down
2 changes: 1 addition & 1 deletion test_files
32 changes: 22 additions & 10 deletions xls.js
Expand Up @@ -3,7 +3,7 @@
/*jshint funcscope:true */
var XLS = {};
(function(XLS){
XLS.version = '0.6.19';
XLS.version = '0.6.20';
var current_codepage = 1252, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('codepage');
Expand Down Expand Up @@ -6430,22 +6430,31 @@ function format_cell(cell, v) {
try { return (cell.w = SSF.format(cell.XF.ifmt||0, v)); } catch(e) { return v; }
}

function sheet_to_row_object_array(sheet, opts){
var val, row, r, hdr = {}, isempty, R, C, v;
function sheet_to_json(sheet, opts){
var val, row, range, header, offset = 1, r, hdr = {}, isempty, R, C, v;
var out = [];
opts = opts || {};
if(!sheet || !sheet["!ref"]) return out;
r = decode_range(sheet["!ref"]);
range = opts.range || sheet["!ref"];
header = opts.header || "";
switch(typeof range) {
case 'string': r = decode_range(range); break;
case 'number': r = decode_range(sheet["!ref"]); r.s.r = range; break;
default: r = range;
}
if(header) offset = 0;
for(R=r.s.r, C = r.s.c; C <= r.e.c; ++C) {
val = sheet[encode_cell({c:C,r:R})];
if(!val) continue;
hdr[C] = format_cell(val);
if(header === "A") hdr[C] = encode_col(C);
else if(header === 1) hdr[C] = C;
else if(Array.isArray(header)) hdr[C] = header[C - r.s.c];
else if(!val) continue;
else hdr[C] = format_cell(val);
}

for (R = r.s.r + 1; R <= r.e.r; ++R) {
for (R = r.s.r + offset; R <= r.e.r; ++R) {
isempty = true;
/* row index available as __rowNum__ */
row = Object.create({ __rowNum__ : R });
row = header === 1 ? [] : Object.create({ __rowNum__ : R });
for (C = r.s.c; C <= r.e.c; ++C) {
val = sheet[encode_cell({c: C,r: R})];
if(!val || !val.t) continue;
Expand All @@ -6466,6 +6475,8 @@ function sheet_to_row_object_array(sheet, opts){
return out;
}

function sheet_to_row_object_array(sheet, opts) { if(!opts) opts = {}; delete opts.range; return sheet_to_json(sheet, opts); }

function sheet_to_csv(sheet, opts) {
var out = [], txt = "";
opts = opts || {};
Expand Down Expand Up @@ -6515,9 +6526,10 @@ var utils = {
decode_range: decode_range,
sheet_to_csv: sheet_to_csv,
make_csv: sheet_to_csv,
make_json: sheet_to_row_object_array,
make_json: sheet_to_json,
get_formulae: get_formulae,
format_cell: format_cell,
sheet_to_json: sheet_to_json,
sheet_to_row_object_array: sheet_to_row_object_array
};
XLS.parse_xlscfb = parse_xlscfb;
Expand Down

0 comments on commit 2bd2d1e

Please sign in to comment.