jsonexport
Version:
Makes easy to convert JSON to CSV
210 lines (189 loc) • 5.61 kB
JavaScript
/* jshint node:true */
'use strict';
const helper = require('../core/helper');
class Handler {
constructor(options) {
this._options = options;
// an object of {typeName:(value,index,parent)=>any}
this._options.typeHandlers = this._options.typeHandlers || {};
}
/**
* Check if results needing mapping to alternate value
*
* @returns [{item, value}] result
*/
_setHeaders(result, item) {
let self = this;
if (!item) return result;
return result.map(function(element) {
element.item = element.item ? item + self._options.headerPathString + element.item : item;
return element;
});
}
castValue(element, item, index, parent){
//cast by matching constructor
const types = this._options.typeHandlers;
for (let type in types ) {
if( isInstanceOfTypeName(element,type) ){
element = types[type].call(types, element, index, parent);
break;//first match we move on
}
}
return element;
}
checkComplex(element, item){
//Check if element is a Date
if (helper.isDate(element)) {
return [{
item: item,
value: (this._options.handleDate || this._handleDate)(element, item),
}];
}
//Check if element is an Array
else if (helper.isArray(element)) {
var resultArray = this._handleArray(element, item);
return this._setHeaders(resultArray, item);
}
//Check if element is a Object
else if (helper.isObject(element)) {
var resultObject = this._handleObject(element);
return this._setHeaders(resultObject, item);
}
return [{
item: item,
value: '',
}];
}
/**
* Check the element type of the element call the correct handle function
*
* @param element Element that will be checked
* @param item Used to make the headers/path breadcrumb
* @returns [{item, value}] result
*/
check(element, item, index, parent) {
element = this.castValue(element, item, index, parent);
// try simple value by highier performance switch
switch(typeof element){
case 'string':
return [{
item: item,
value: this._handleString(element, item),
}];
case 'number':
return [{
item: item,
value: this._handleNumber(element, item),
}];
case 'boolean':
return [{
item: item,
value: this._handleBoolean.bind(this)(element, item),
}];
}
return this.checkComplex(element, item);
}
/**
* Handle all Objects
*
* @param {Object} obj
* @returns [{item, value}] result
*/
_handleObject(obj) {
var result = [];
//Look every object props
for (var prop in obj) {
var propData = obj[prop];
//Check the propData type
var resultCheckType = this.check(propData, prop, prop, obj);
//Append to results aka merge results aka array-append-array
result = result.concat(resultCheckType);
}
return result;
}
/**
* Handle all Arrays, merges arrays with primitive types in a single value
*
* @param {Array} array
* @returns [{item, value}] result
*/
_handleArray(array) {
let self = this;
let result = [];
var firstElementWithoutItem;
for (let aIndex=0; aIndex < array.length; ++aIndex) {
let element = array[aIndex];
//Check the propData type
var resultCheckType = self.check(element, null, aIndex, array);
//Check for results without itens, merge all itens with the first occurrence
if (resultCheckType.length === 0) continue;
var firstResult = resultCheckType[0];
if (!firstResult.item && firstElementWithoutItem !== undefined) {
firstElementWithoutItem.value += self._options.arrayPathString + firstResult.value;
continue;
} else if (resultCheckType.length > 0 && !firstResult.item && firstElementWithoutItem === undefined) {
firstElementWithoutItem = firstResult;
}
//Append to results
result = result.concat(resultCheckType);
}
return result;
}
/**
* Handle all Boolean variables, can be replaced with options.handleBoolean
*
* @param {Boolean} boolean
* @returns {String} result
*/
_handleBoolean(boolean) {
var result;
//Check for booolean options
if (boolean) {
result = this._options.booleanTrueString || 'true';
} else {
result = this._options.booleanFalseString || 'false';
}
return result;
}
/**
* Handle all String variables, can be replaced with options.handleString
*
* @param {String} string
* @returns {String} string
*/
_handleString(string) {
return string;
}
/**
* Handle all Number variables, can be replaced with options.handleNumber
*
* @param {Number} number
* @returns {Number} number
*/
_handleNumber(number) {
return number;
}
/**
* Handle all Date variables, can be replaced with options.handleDate
*
* @param {Date} number
* @returns {string} result
*/
_handleDate(date) {
return date.toLocaleDateString();
}
}
module.exports = Handler;
const globalScope = typeof(window)==="undefined" ? global : window;
function isInstanceOfTypeName(element, typeName){
if( element instanceof globalScope[typeName] ){
return true;//Buffer and complex objects
}
//literals in javascript cannot be checked by instance of
switch( typeof(element) ){
case 'string':return typeName==="String";
case 'boolean':return typeName==="Boolean";
case 'number':return typeName==="Number";
}
return false;
}