299 lines
10 KiB
JavaScript
299 lines
10 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
/**
|
|
* Copyright (C) 2016-2019 Michael Kourlas
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
var xmlcreate_1 = require("xmlcreate");
|
|
var options_1 = require("./options");
|
|
var utils_1 = require("./utils");
|
|
/**
|
|
* Indicates that an object of a particular type should be suppressed from the
|
|
* XML output.
|
|
*
|
|
* See the `typeHandlers` property in {@link IOptions} for more details.
|
|
*/
|
|
var Absent = /** @class */ (function () {
|
|
function Absent() {
|
|
}
|
|
Object.defineProperty(Absent, "instance", {
|
|
/**
|
|
* Returns the sole instance of Absent.
|
|
*/
|
|
get: function () {
|
|
return Absent._instance;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
Absent._instance = new Absent();
|
|
return Absent;
|
|
}());
|
|
exports.Absent = Absent;
|
|
/**
|
|
* Gets the type handler associated with a value.
|
|
*
|
|
* @private
|
|
*/
|
|
function getHandler(value, options) {
|
|
var type = Object.prototype.toString.call(value);
|
|
var handler;
|
|
if (Object.prototype.hasOwnProperty.call(options.typeHandlers, "*")) {
|
|
handler = options.typeHandlers["*"];
|
|
}
|
|
if (Object.prototype.hasOwnProperty.call(options.typeHandlers, type)) {
|
|
handler = options.typeHandlers[type];
|
|
}
|
|
return handler;
|
|
}
|
|
/**
|
|
* Parses a string into XML and adds it to the parent element or attribute.
|
|
*
|
|
* @private
|
|
*/
|
|
function parseString(str, parentElement, options) {
|
|
var requiresCdata = function (s) {
|
|
return (options.cdataInvalidChars && (s.indexOf("<") !== -1
|
|
|| s.indexOf("&") !== -1))
|
|
|| options.cdataKeys.indexOf(parentElement.name) !== -1
|
|
|| options.cdataKeys.indexOf("*") !== -1;
|
|
};
|
|
if (parentElement instanceof xmlcreate_1.XmlElement) {
|
|
if (requiresCdata(str)) {
|
|
var cdataStrs = str.split("]]>");
|
|
for (var i = 0; i < cdataStrs.length; i++) {
|
|
if (requiresCdata(cdataStrs[i])) {
|
|
parentElement.cdata({
|
|
charData: cdataStrs[i],
|
|
replaceInvalidCharsInCharData: options.replaceInvalidChars
|
|
});
|
|
}
|
|
else {
|
|
parentElement.charData({
|
|
charData: cdataStrs[i],
|
|
replaceInvalidCharsInCharData: options.replaceInvalidChars
|
|
});
|
|
}
|
|
if (i < cdataStrs.length - 1) {
|
|
parentElement.charData({
|
|
charData: "]]>",
|
|
replaceInvalidCharsInCharData: options.replaceInvalidChars
|
|
});
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
parentElement.charData({
|
|
charData: str,
|
|
replaceInvalidCharsInCharData: options.replaceInvalidChars
|
|
});
|
|
}
|
|
}
|
|
else {
|
|
parentElement.text({
|
|
charData: str,
|
|
replaceInvalidCharsInCharData: options.replaceInvalidChars
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Parses an attribute into XML and adds it to the parent element.
|
|
*
|
|
* @private
|
|
*/
|
|
function parseAttribute(name, value, parentElement, options) {
|
|
var attribute = parentElement.attribute({
|
|
name: name,
|
|
replaceInvalidCharsInName: options.replaceInvalidChars
|
|
});
|
|
parseString(utils_1.stringify(value), attribute, options);
|
|
}
|
|
/**
|
|
* Parses an object or Map entry into XML and adds it to the parent element.
|
|
*
|
|
* @private
|
|
*/
|
|
function parseObjectOrMapEntry(key, value, parentElement, options) {
|
|
// Alias key
|
|
if (key === options.aliasString) {
|
|
parentElement.name = utils_1.stringify(value);
|
|
return;
|
|
}
|
|
// Attributes key
|
|
if (key.indexOf(options.attributeString) === 0 && utils_1.isObject(value)) {
|
|
for (var _i = 0, _a = Object.keys(value); _i < _a.length; _i++) {
|
|
var subkey = _a[_i];
|
|
parseAttribute(subkey, utils_1.stringify(value[subkey]), parentElement, options);
|
|
}
|
|
return;
|
|
}
|
|
// Value key
|
|
if (key.indexOf(options.valueString) === 0) {
|
|
parseValue(key, utils_1.stringify(value), parentElement, options);
|
|
return;
|
|
}
|
|
// Standard handling (create new element for entry)
|
|
var element = parentElement;
|
|
if (!utils_1.isArray(value) && !utils_1.isSet(value)) {
|
|
// If handler for value returns absent, then do not add element
|
|
var handler = getHandler(value, options);
|
|
if (!utils_1.isUndefined(handler)) {
|
|
if (handler(value) === Absent.instance) {
|
|
return;
|
|
}
|
|
}
|
|
element = parentElement.element({
|
|
name: key,
|
|
replaceInvalidCharsInName: options.replaceInvalidChars,
|
|
useSelfClosingTagIfEmpty: options.useSelfClosingTagIfEmpty
|
|
});
|
|
}
|
|
parseValue(key, value, element, options);
|
|
}
|
|
/**
|
|
* Parses an Object or Map into XML and adds it to the parent element.
|
|
*
|
|
* @private
|
|
*/
|
|
function parseObjectOrMap(objectOrMap, parentElement, options) {
|
|
if (utils_1.isMap(objectOrMap)) {
|
|
objectOrMap.forEach(function (value, key) {
|
|
parseObjectOrMapEntry(utils_1.stringify(key), value, parentElement, options);
|
|
});
|
|
}
|
|
else {
|
|
for (var _i = 0, _a = Object.keys(objectOrMap); _i < _a.length; _i++) {
|
|
var key = _a[_i];
|
|
parseObjectOrMapEntry(key, objectOrMap[key], parentElement, options);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Parses an array or Set into XML and adds it to the parent element.
|
|
*
|
|
* @private
|
|
*/
|
|
function parseArrayOrSet(key, arrayOrSet, parentElement, options) {
|
|
var arrayNameFunc;
|
|
if (Object.prototype.hasOwnProperty.call(options.wrapHandlers, "*")) {
|
|
arrayNameFunc = options.wrapHandlers["*"];
|
|
}
|
|
if (Object.prototype.hasOwnProperty.call(options.wrapHandlers, key)) {
|
|
arrayNameFunc = options.wrapHandlers[key];
|
|
}
|
|
var arrayKey = key;
|
|
var arrayElement = parentElement;
|
|
if (!utils_1.isUndefined(arrayNameFunc)) {
|
|
var arrayNameFuncKey = arrayNameFunc(arrayKey, arrayOrSet);
|
|
if (!utils_1.isNull(arrayNameFuncKey)) {
|
|
arrayKey = arrayNameFuncKey;
|
|
arrayElement = parentElement.element({
|
|
name: key,
|
|
replaceInvalidCharsInName: options.replaceInvalidChars,
|
|
useSelfClosingTagIfEmpty: options.useSelfClosingTagIfEmpty
|
|
});
|
|
}
|
|
}
|
|
arrayOrSet.forEach(function (item) {
|
|
var element = arrayElement;
|
|
if (!utils_1.isArray(item) && !utils_1.isSet(item)) {
|
|
// If handler for value returns absent, then do not add element
|
|
var handler = getHandler(item, options);
|
|
if (!utils_1.isUndefined(handler)) {
|
|
if (handler(item) === Absent.instance) {
|
|
return;
|
|
}
|
|
}
|
|
element = arrayElement.element({
|
|
name: arrayKey,
|
|
replaceInvalidCharsInName: options.replaceInvalidChars,
|
|
useSelfClosingTagIfEmpty: options.useSelfClosingTagIfEmpty
|
|
});
|
|
}
|
|
parseValue(arrayKey, item, element, options);
|
|
});
|
|
}
|
|
/**
|
|
* Parses an arbitrary JavaScript value into XML and adds it to the parent
|
|
* element.
|
|
*
|
|
* @private
|
|
*/
|
|
function parseValue(key, value, parentElement, options) {
|
|
// If a handler for a particular type is user-defined, use that handler
|
|
// instead of the defaults
|
|
var handler = getHandler(value, options);
|
|
if (!utils_1.isUndefined(handler)) {
|
|
value = handler(value);
|
|
}
|
|
if (utils_1.isObject(value) || utils_1.isMap(value)) {
|
|
parseObjectOrMap(value, parentElement, options);
|
|
return;
|
|
}
|
|
if (utils_1.isArray(value) || utils_1.isSet(value)) {
|
|
parseArrayOrSet(key, value, parentElement, options);
|
|
return;
|
|
}
|
|
parseString(utils_1.stringify(value), parentElement, options);
|
|
}
|
|
/**
|
|
* Converts the specified object to XML and adds the XML representation to the
|
|
* specified XmlDocument object using the specified options.
|
|
*
|
|
* This function does not add a root element. In addition, it does not add an
|
|
* XML declaration or DTD, and the associated options in {@link IOptions} are
|
|
* ignored. If desired, these must be added manually.
|
|
*/
|
|
function parseToExistingElement(element, object, options) {
|
|
var opts = new options_1.Options(options);
|
|
parseValue(element.name, object, element, opts);
|
|
}
|
|
exports.parseToExistingElement = parseToExistingElement;
|
|
/**
|
|
* Returns a XML string representation of the specified object using the
|
|
* specified options.
|
|
*
|
|
* `root` is the name of the root XML element. When the object is converted
|
|
* to XML, it will be a child of this root element.
|
|
*/
|
|
function parse(root, object, options) {
|
|
var opts = new options_1.Options(options);
|
|
var document = new xmlcreate_1.XmlDocument({
|
|
validation: opts.validation
|
|
});
|
|
if (opts.declaration.include) {
|
|
document.decl(opts.declaration);
|
|
}
|
|
if (opts.dtd.include) {
|
|
document.dtd({
|
|
// Validated in options.ts
|
|
// @formatter:off
|
|
// eslint-disable-next-line max-len
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
name: opts.dtd.name,
|
|
// @formatter:on
|
|
pubId: opts.dtd.pubId,
|
|
sysId: opts.dtd.sysId
|
|
});
|
|
}
|
|
var rootElement = document.element({
|
|
name: root,
|
|
replaceInvalidCharsInName: opts.replaceInvalidChars,
|
|
useSelfClosingTagIfEmpty: opts.useSelfClosingTagIfEmpty
|
|
});
|
|
parseToExistingElement(rootElement, object, options);
|
|
return document.toString(opts.format);
|
|
}
|
|
exports.parse = parse;
|