xml-from-object
Version:
Easy, highly typed and customizable way to build your xml using JS/TS plain objects.
127 lines (109 loc) • 4.68 kB
text/typescript
import { isArrayOfObject, isArrayOfString } from "./helpers";
import {
FromObjectHeader,
SchemaFieldConfig,
FromObjectInternalValue,
FromObjectParams,
FromObjectSchema,
SchemaFieldAttributes,
} from "./types";
/**
* Class way to transform a Javascript plain object into XML
*/
export class XmlFromObject {
/**
* Transform a Javascript plain object into XML
* @param {object} schemaConfig.schema - The typed schema with fields and configs to create the XML.
* @param {object} [schemaConfig.header] - (Optional) The xml header (typically "<?xml version...>").
*
*/
fromObject = xmlFromObject;
}
/**
* Functional way to transform a Javascript plain object into XML
* @param {object} schemaConfig.schema - The typed schema with fields and configs to create the XML.
* @param {object} [schemaConfig.header] - (Optional) The xml header (typically "<?xml version...>").
*/
export function xmlFromObject(schemaConfig: FromObjectParams): string {
if (!schemaConfig.header) return parseSchemaToXml(schemaConfig.schema);
const parsedHeader = parseXmlHeader(schemaConfig.header);
const parsedSchema = parseSchemaToXml(schemaConfig.schema);
return parsedHeader + parsedSchema;
}
function parseXmlHeader(header: FromObjectHeader): string {
if (header.custom !== undefined) return header.custom;
return `<?xml version="${header.version}" encoding="${header.encoding}"?>`;
}
function parseSchemaToXml(schema: FromObjectSchema): string {
const resultInArray = Object.entries(schema).map(([key, { value: untypedValue, ...fieldConfig }]) => {
const { value, type } = getValueType(untypedValue);
switch (type) {
case "array-of-object":
return parseTagForArrayOfObjects(key, value, fieldConfig);
case "array-of-string":
return parseTagForArrayOfString(key, value, fieldConfig);
case "object":
return parseTagForObject(key, value, fieldConfig);
case "string":
return parseTagForString(key, value, fieldConfig);
case "self-closing":
return parseSelfClosingTag(key);
}
});
return resultInArray.join("");
}
function getValueType(value: any): FromObjectInternalValue {
if (value === undefined || value === null) return { type: "self-closing", value: undefined };
if (isArrayOfObject(value)) return { type: "array-of-object", value: value as FromObjectSchema[] };
if (isArrayOfString(value)) return { type: "array-of-string", value: value as string[] };
if (typeof value === "object") return { type: "object", value: value as FromObjectSchema };
return { type: "string", value: String(value) };
}
function parseTagForArrayOfObjects(key: string, values: FromObjectSchema[], config: SchemaFieldConfig): string {
const nestedTagsAsArrayOfString = values.map(parseSchemaToXml);
const nestedTags = nestedTagsAsArrayOfString.join("");
return keyValueToXmlTag(key, nestedTags, config);
}
function parseTagForArrayOfString(key: string, value: string[], config: SchemaFieldConfig): string {
return keyValueToXmlTag(key, value.join(), config);
}
function parseTagForObject(key: string, value: FromObjectSchema, config: SchemaFieldConfig): string {
const nestedValue = parseSchemaToXml(value);
return keyValueToXmlTag(key, nestedValue, config);
}
function parseTagForString(key: string, value: string, config: SchemaFieldConfig): string {
return keyValueToXmlTag(key, value, config);
}
function parseSelfClosingTag(key: string): string {
return `<${key}/>`;
}
function keyValueToXmlTag(key: string, value: string, config: SchemaFieldConfig): string {
const parsedKey = parseKey(key, config);
const parsedValue = parseValue(value, config);
return parsedKey.start + parsedValue + parsedKey.end;
}
function parseValue(value: string, config: SchemaFieldConfig): string {
if (config.options?.cdata) return wrapWithCdata(value);
return value;
}
function wrapWithCdata(value: string): string {
return `<![CDATA[${value}]]>`;
}
function parseKey(key: string, config: SchemaFieldConfig): { start: string; end: string } {
const start = parseStartOfKey(key, config);
const end = `</${key}>`;
return { start, end };
}
function parseStartOfKey(key: string, config: SchemaFieldConfig): string {
if (!config.attributes) return `<${key}>`;
return `<${key} ${parseKeyAttributes(config.attributes)}>`;
}
function parseKeyAttributes(attributes: SchemaFieldAttributes): string {
const attributesInArray = Object.entries(attributes).map(([key, value]) => {
return parseAttribute(key, value);
});
return attributesInArray.join(" ");
}
function parseAttribute(key: string, value: string): string {
return `${key}="${value}"`;
}