Skip to content

Commit

Permalink
Committed by npm script.
Browse files Browse the repository at this point in the history
  • Loading branch information
nashwaan committed Dec 14, 2017
1 parent 99cb8fd commit cdef0a7
Show file tree
Hide file tree
Showing 4 changed files with 431 additions and 28 deletions.
1 change: 0 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [

{
"type": "node",
"request": "launch",
Expand Down
88 changes: 72 additions & 16 deletions lib/xml2js.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ function validateOptions(userOptions) {
helper.ensureKeyExists('name', options);
helper.ensureKeyExists('elements', options);
helper.ensureKeyExists('parent', options);
helper.checkFnExists('doctype', options);
helper.checkFnExists('instruction', options);
helper.checkFnExists('cdata', options);
helper.checkFnExists('comment', options);
helper.checkFnExists('text', options);
helper.checkFnExists('instructionName', options);
helper.checkFnExists('elementName', options);
helper.checkFnExists('attributeName', options);
helper.checkFnExists('attributeValue', options);
helper.checkFnExists('attributes', options);
return options;
}

Expand All @@ -52,14 +62,31 @@ function nativeType(value) {
return value;
}

function addField(type, value, options) {
function addField(type, value) {
var key;
if (options.compact) {
if (!currentElement[options[type + 'Key']] && options.alwaysArray) {
currentElement[options[type + 'Key']] = [];
}
if (currentElement[options[type + 'Key']] && !(currentElement[options[type + 'Key']] instanceof Array)) {
currentElement[options[type + 'Key']] = [currentElement[options[type + 'Key']]];
}
if (type + 'Fn' in options && typeof value === 'string') {
value = options[type + 'Fn'](value);
}
if (type === 'instruction' && ('instructionFn' in options || 'instructionNameFn' in options)) {
for (key in value) {
if (value.hasOwnProperty(key)) {
if ('instructionFn' in options) {
value[key] = options.instructionFn(value[key], key, currentElement);
} else {
var temp = value[key];
delete value[key];
value[options.instructionNameFn(key, currentElement)] = temp;
}
}
}
}
if (currentElement[options[type + 'Key']] instanceof Array) {
currentElement[options[type + 'Key']].push(value);
} else {
Expand All @@ -69,21 +96,30 @@ function addField(type, value, options) {
if (!currentElement[options.elementsKey]) {
currentElement[options.elementsKey] = [];
}
var key, element = {};
var element = {};
element[options.typeKey] = type;
if (type === 'instruction' && typeof value === 'object') {
if (type === 'instruction') {
for (key in value) {
if (value.hasOwnProperty(key)) {
break;
}
}
element[options.nameKey] = key;
element[options.nameKey] = 'instructionNameFn' in options ? options.instructionNameFn(key, currentElement) : key;
if (options.instructionHasAttributes) {
element[options.attributesKey] = value[key][options.attributesKey];
if ('attributeNameFn' in options) {
element[options.attributesKey] = options[type + 'Fn'](element[options.attributesKey]);
}
} else {
if (type + 'Fn' in options) {
value[key] = options[type + 'Fn'](value[key]);
}
element[options[type + 'Key']] = value[key];
}
} else {
if (type + 'Fn' in options) {
value = options[type + 'Fn'](value);
}
element[options[type + 'Key']] = value;
}
if (options.addParent) {
Expand All @@ -93,6 +129,27 @@ function addField(type, value, options) {
}
}

function manipulateAttributes(attributes) {
if ('attributesFn' in options && attributes) {
attributes = options.attributesFn(attributes, currentElement);
}
if ((options.trim || 'attributeValueFn' in options || 'attributeNameFn' in options) && attributes) {
var key;
for (key in attributes) {
if (attributes.hasOwnProperty(key)) {
if (options.trim) attributes[key] = attributes[key].trim();
if ('attributeValueFn' in options) attributes[key] = options.attributeValueFn(attributes[key], key, currentElement);
if ('attributeNameFn' in options) {
var temp = attributes[key];
delete attributes[key];
attributes[options.attributeNameFn(key, attributes[key], currentElement)] = temp;
}
}
}
}
return attributes;
}

function onInstruction(instruction) {
var attributes = {};
if (instruction.body && (instruction.name.toLowerCase() === 'xml' || options.instructionHasAttributes)) {
Expand All @@ -101,6 +158,7 @@ function onInstruction(instruction) {
while ((match = attrsRegExp.exec(instruction.body)) !== null) {
attributes[match[1]] = match[2] || match[3] || match[4];
}
attributes = manipulateAttributes(attributes);
}
if (instruction.name.toLowerCase() === 'xml') {
if (options.ignoreDeclaration) {
Expand All @@ -127,27 +185,25 @@ function onInstruction(instruction) {
} else {
value[instruction.name] = instruction.body;
}
addField('instruction', value, options);
addField('instruction', value);
}
}

function onStartElement(name, attributes) {
var key, element;
var element;
if (typeof name === 'object') {
attributes = name.attributes;
name = name.name;
}
if (options.trim && attributes) {
for (key in attributes) {
if (attributes.hasOwnProperty(key)) {
attributes[key] = attributes[key].trim();
}
}
attributes = manipulateAttributes(attributes);
if ('elementNameFn' in options) {
name = options.elementNameFn(name, currentElement);
}
if (options.compact) {
element = {};
if (!options.ignoreAttributes && attributes && Object.keys(attributes).length) {
element[options.attributesKey] = {};
var key;
for (key in attributes) {
if (attributes.hasOwnProperty(key)) {
element[options.attributesKey][key] = attributes[key];
Expand Down Expand Up @@ -202,7 +258,7 @@ function onText(text) {
if (options.sanitize) {
text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
addField('text', text, options);
addField('text', text);
}

function onComment(comment) {
Expand All @@ -212,7 +268,7 @@ function onComment(comment) {
if (options.trim) {
comment = comment.trim();
}
addField('comment', comment, options);
addField('comment', comment);
}

function onEndElement(name) {
Expand All @@ -230,7 +286,7 @@ function onCdata(cdata) {
if (options.trim) {
cdata = cdata.trim();
}
addField('cdata', cdata, options);
addField('cdata', cdata);
}

function onDoctype(doctype) {
Expand All @@ -241,7 +297,7 @@ function onDoctype(doctype) {
if (options.trim) {
doctype = doctype.trim();
}
addField('doctype', doctype, options);
addField('doctype', doctype);
}

function onError(error) {
Expand Down
75 changes: 64 additions & 11 deletions test/test-items.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ var cases = [
js1: {"_declaration":{},"a":{"b":{}}},
js2: {"declaration":{},"elements":[{"type":"element","name":"a","elements":[{"type":"element","name":"b"}]}]}
}, {
desc: 'processing instruction <?go there>',
desc: 'processing instruction <?go there?>',
xml: '<?go there?>',
js1: {"_instruction":{"go": "there"}},
js2: {"elements":[{"type":"instruction","name":"go","instruction":"there"}]}
}, {
desc: '2 processing instructions <?go there?><?come here?>',
xml: '<?go there?><?come here?>',
js1: {"_instruction":[{"go": "there"},{"come": "here"}]},
js2: {"elements":[{"type":"instruction","name":"go","instruction":"there"},{"type":"instruction","name":"come","instruction":"here"}]}
}, {
desc: 'should convert comment',
xml: '<!-- \t Hello, World! \t -->',
Expand Down Expand Up @@ -100,21 +105,24 @@ var cases = [
module.exports = function (direction, options) {
var i, tests = [];
options = options || {};
function applyOptions (obj, fullKey) {
var key, fn;
function applyOptions (obj, pathKey) {
var key, fullKey;
pathKey = pathKey || '';
if (obj instanceof Array) {
obj = obj.filter(function (el) {
return !(options.ignoreText && el.type === 'text' || options.ignoreComment && el.type === 'comment' || options.ignoreCdata && el.type === 'cdata'
|| options.ignoreDoctype && el.type === 'doctype' || options.ignoreDeclaration && el.type === 'declaration' || options.ignoreInstruction && el.type === 'instruction');
}).map(function (el) {
return manipulate(el, fullKey);
return manipulate(el, pathKey);
});
} else if (typeof obj === 'object') {
for (key in obj) {
fullKey = (fullKey? fullKey + '.' : '') + key;
fullKey = (pathKey ? pathKey + '.' : '') + key;
if (options.compact && options.alwaysArray && !(obj[key] instanceof Array) && key !== '_declaration' && (key === '_instruction' || fullKey.indexOf('_instruction') < 0) && fullKey.indexOf('_attributes') < 0) {
obj[key] = [obj[key]];
}
key = applyNameCallbacks(obj, key, pathKey.split('.').pop());
key = applyAttributesCallback(obj, key, pathKey.split('.').pop());
if (key.indexOf('_') === 0 && obj[key] instanceof Array) {
obj[key] = obj[key].map(function (el) {
return manipulate(el, fullKey);
Expand Down Expand Up @@ -154,10 +162,13 @@ module.exports = function (direction, options) {
// }
}
return obj;
function manipulate(x, key) {
if (x instanceof Array || typeof x === 'object') {
return applyOptions(x, key);
function manipulate(x, fullKey) {
if (x instanceof Array) {
return applyOptions(x, fullKey);
} if (typeof x === 'object') {
return applyOptions(x, fullKey);
} else if (typeof x === 'string') {
x = applyValueCallbacks(x, fullKey.split('.').pop(), fullKey.split('.')[fullKey.split('.').length - 2] || '');
return options.trim? x.trim() : x;
} else if (typeof x === 'number' || typeof x === 'boolean') {
return options.nativeType? x.toString() : x;
Expand All @@ -176,15 +187,57 @@ module.exports = function (direction, options) {
}
return js;
}
function applyNameCallbacks(obj, key, parentKey) {
if ('instructionNameFn' in options && (options.compact && parentKey === '_instruction' || !options.compact && obj.type === 'instruction')
|| 'elementNameFn' in options && (options.compact && key.indexOf('_') < 0 && parentKey !== '_attributes' && parentKey !== '_instruction' || !options.compact && obj.type === 'element')) {
if (options.compact) {
var temp = obj[key];
delete obj[key];
key = 'elementNameFn' in options ? options.elementNameFn(key) : options.instructionNameFn(key);
obj[key] = temp;
} else {
obj.name = 'elementNameFn' in options ? options.elementNameFn(obj.name) : options.instructionNameFn(obj.name);
}
}
return key;
}
function applyAttributesCallback(obj, key, parentKey) {
if (('attributeNameFn' in options || 'attributeValueFn' in options) && (parentKey === '_attributes' || parentKey === 'attributes')) {
if ('attributeNameFn' in options) {
var temp = obj[key];
delete obj[key];
key = options.attributeNameFn(key);
obj[key] = temp;
}
if ('attributeValueFn' in options) {
obj[key] = options.attributeValueFn(obj[key]);
}
}
if ('attributesFn' in options && (key === '_attributes' || key === 'attributes')) {
obj[key] = options.attributesFn(obj[key]);
}
return key;
}
function applyValueCallbacks(value, key, parentKey) {
var fn;
for (fn in options) {
if (fn.match(/Fn$/) && !fn.match(/NameFn$/)) {
var callbackName = (options.compact ? '_' : '') + fn.replace('Fn', '');
if (key === callbackName || parentKey === callbackName) {
value = options[fn](value);
}
}
}
return value;
}
for (i = 0; i < cases.length; ++i) {
tests.push({desc: cases[i].desc, xml: null, js: null});
tests[i].js = options.compact ? cases[i].js1 : cases[i].js2;
tests[i].xml = cases[i].xml;
if (direction === 'xml2js') {
tests[i].js = applyOptions(JSON.parse(JSON.stringify(tests[i].js)));
tests[i].js = applyKeyNames(tests[i].js);
}
tests[i].xml = cases[i].xml;
if (direction === 'js2xml') {
} else if (direction === 'js2xml') {
if (!('spaces' in options) || options.spaces === 0 || typeof options.spaces === 'boolean') { tests[i].xml = tests[i].xml.replace(/>\n\v*/gm, '>'); }
if ('spaces' in options && options.spaces !== 0 && typeof options.spaces === 'number') { tests[i].xml = tests[i].xml.replace(/\v/g, Array(options.spaces + 1).join(' ')); }
if ('spaces' in options && typeof options.spaces === 'string') { tests[i].xml = tests[i].xml.replace(/\v/g, options.spaces); }
Expand Down
Loading

0 comments on commit cdef0a7

Please sign in to comment.