diffusion
Version:
Diffusion JavaScript client
176 lines (144 loc) • 4.71 kB
JavaScript
/*
* A module for converting between JSON and XML representations for
* the primary purpose of topic metadata.
*/
var ATTR = require('../schema/consts').ATTR;
// Parse a string of key/value pairs and produce a js object.
// NB: Does not handle embedded = or "
function attrs_str2js(str) {
var js = {};
var pairs = str.split(' ');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
js[pair[0]] = pair[1].match(/"(.*)"/)[1];
}
return js;
}
function escapeEntities(str) {
return String(str).replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function attrs_obj2arr(obj) {
var attrs = [];
if (typeof obj === 'object') {
for (var key in obj) {
attrs.push('' + key + '="' + escapeEntities(obj[key]) + '"');
}
}
return attrs;
}
// This is a simple XML parser that can handle topic metadata received
// from Diffusion. It's not a fully-fledged XML parser, but it's a lot
// more lightweight.
function xml2json(xml, pos) {
var js = {};
if (pos === undefined) {
// First call of this function.
pos = {
idx : 0,
isRoot : true
};
// Strip off preamble. Because it's the first thing to be
// found in the XML, we can limit searching for it to the
// initial call to this function.
var preamble = xml.match(/<\?.*?\?>/);
if (preamble) {
xml = xml.substr(preamble[0].length);
}
}
var elem_match = null;
for (;;) {
elem_match = xml.substr(pos.idx).match(/<.*?>/);
if (!elem_match) {
break;
}
pos.idx += elem_match[0].length;
var elem = elem_match[0].trim();
if (elem.substr(0,2) === '</') {
// Closing element, back to parent.
return js;
}
var name = elem.match(/\b(.+?)\b/)[0];
var attrs_match = elem.match(/<.+?\b(.*)\/?>/);
var attrs_str = '';
if (attrs_match) {
attrs_str = attrs_match[1].trim();
}
var attrs = attrs_str2js(attrs_str);
var child_count;
if (elem.substr(-2) === '/>') {
// Self-closing element, no children.
// Root element, never repeated.
if (pos.isRoot) {
pos.isRoot = false;
js[name] = xml2json(xml, pos);
js[name][ATTR] = attrs;
} else {
if (!js[name]) {
js[name] = [];
}
child_count = js[name].push({});
js[name][child_count - 1][ATTR] = attrs;
}
} else {
// Opening a new element, which may have children.
// Root element is never repeated.
if (pos.isRoot) {
pos.isRoot = false;
js[name] = xml2json(xml, pos);
js[name][ATTR] = attrs;
} else {
if (!js[name]) {
js[name] = [];
}
child_count = js[name].push(xml2json(xml, pos));
js[name][child_count - 1][ATTR] = attrs;
}
}
}
return js;
}
// Convert a JSON object (primarily, topic metadata) to a stringified
// XML representation. It is not intended to be a complete JSON->XML
// converter, but it is small, light, and does enough for our needs.
function json2xml(obj, recur) {
var str = '';
if (!recur) {
str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
}
for (var key in obj) {
if (key === ATTR) {
continue;
}
var val = obj[key];
if (!(val instanceof Array)) {
val = Array(val);
}
for (var i = 0; i < val.length; i++) {
var attrs = attrs_obj2arr(val[i][ATTR]);
str += '<' + key;
if (attrs && attrs.length > 0) {
str += ' ';
str += attrs.join(' ');
}
// Check for non-attribute children of this element
var child_str = json2xml(val[i], true);
if (child_str && child_str.length > 0) {
str += '>';
str += child_str;
str += '</' + key + '>';
} else {
// No children, close tag here.
str += '/>';
}
}
}
return str;
}
module.exports = {
xml2json : xml2json,
json2xml : json2xml
};