BREAKING CHANGE(core): Implement custom XmlBuilder, remove xmlbuilder2, upgrade fast-xml-parser, update SmartXml API, tests and CI
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartxml',
|
||||
version: '1.1.1',
|
||||
version: '2.0.0',
|
||||
description: 'A package for creating and parsing XML formatted files.'
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import * as plugins from './smartxml.plugins.js';
|
||||
import { XmlBuilder } from './smartxml.xmlbuilder.js';
|
||||
|
||||
export class SmartXml {
|
||||
constructor() {}
|
||||
|
||||
public create = plugins.xmlbuilder2.create;
|
||||
public create = XmlBuilder.create;
|
||||
|
||||
public parseXmlToObject<T = any>(xmlStringArg: string): T {
|
||||
const parser = new plugins.fastXmlParser.XMLParser({
|
||||
@@ -22,9 +23,11 @@ export class SmartXml {
|
||||
ignoreAttributes: false,
|
||||
attributeNamePrefix: '@_',
|
||||
format: true,
|
||||
indentBy: ' '
|
||||
indentBy: ' ',
|
||||
});
|
||||
const xml = builder.build(jsObject);
|
||||
return '<?xml version="1.0" encoding="UTF-8"?>\n' + xml;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { XmlBuilder };
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import * as fastXmlParser from 'fast-xml-parser';
|
||||
import * as xmlbuilder2 from 'xmlbuilder2';
|
||||
|
||||
export {
|
||||
fastXmlParser,
|
||||
xmlbuilder2,
|
||||
};
|
||||
export { fastXmlParser };
|
||||
|
||||
180
ts/smartxml.xmlbuilder.ts
Normal file
180
ts/smartxml.xmlbuilder.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import * as plugins from './smartxml.plugins.js';
|
||||
|
||||
/**
|
||||
* A lightweight chainable XML builder that provides a fluent API
|
||||
* for programmatically constructing XML documents.
|
||||
*/
|
||||
export class XmlBuilder {
|
||||
private stack: any[] = [];
|
||||
private current: any = null;
|
||||
private rootElement: any = null;
|
||||
|
||||
/**
|
||||
* Creates a new XML builder instance
|
||||
* @param input Optional: object to convert to XML, or XML string to parse
|
||||
*/
|
||||
constructor(input?: any) {
|
||||
if (input) {
|
||||
if (typeof input === 'string') {
|
||||
// Parse XML string using fast-xml-parser
|
||||
const parser = new plugins.fastXmlParser.XMLParser({
|
||||
preserveOrder: true,
|
||||
ignoreAttributes: false,
|
||||
});
|
||||
this.rootElement = parser.parse(input);
|
||||
this.current = this.rootElement;
|
||||
} else if (typeof input === 'object') {
|
||||
// Accept object input
|
||||
this.rootElement = input;
|
||||
this.current = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method for creating instances
|
||||
*/
|
||||
static create(input?: any): XmlBuilder {
|
||||
return new XmlBuilder(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element
|
||||
* @param name Element name
|
||||
* @param attributes Optional attributes object
|
||||
*/
|
||||
public ele(name: string, attributes?: Record<string, any>): this {
|
||||
if (!this.current) {
|
||||
// First element becomes root
|
||||
this.rootElement = {};
|
||||
this.current = this.rootElement;
|
||||
}
|
||||
|
||||
const newElement: any = {};
|
||||
|
||||
// Add attributes with @_ prefix (fast-xml-parser format)
|
||||
if (attributes) {
|
||||
for (const [key, value] of Object.entries(attributes)) {
|
||||
newElement[`@_${key}`] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this property already exists
|
||||
if (this.current[name]) {
|
||||
// Convert to array if not already
|
||||
if (!Array.isArray(this.current[name])) {
|
||||
this.current[name] = [this.current[name]];
|
||||
}
|
||||
this.current[name].push(newElement);
|
||||
this.stack.push(this.current);
|
||||
this.current = newElement;
|
||||
} else {
|
||||
this.current[name] = newElement;
|
||||
this.stack.push(this.current);
|
||||
this.current = newElement;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add text content to current element
|
||||
* @param content Text content
|
||||
*/
|
||||
public txt(content: string | number): this {
|
||||
if (this.current) {
|
||||
// Check if current element already has properties
|
||||
const hasProperties = Object.keys(this.current).length > 0;
|
||||
if (hasProperties) {
|
||||
// Use #text format for mixed content
|
||||
this.current['#text'] = String(content);
|
||||
} else {
|
||||
// For simple text-only elements, we can use direct assignment
|
||||
// But to maintain consistency, we'll use #text
|
||||
this.current['#text'] = String(content);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add attribute to current element
|
||||
* @param name Attribute name
|
||||
* @param value Attribute value
|
||||
*/
|
||||
public att(name: string, value: any): this {
|
||||
if (this.current) {
|
||||
this.current[`@_${name}`] = value;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move up to parent element
|
||||
*/
|
||||
public up(): this {
|
||||
if (this.stack.length > 0) {
|
||||
this.current = this.stack.pop();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root element (for navigation)
|
||||
*/
|
||||
public root(): this {
|
||||
if (this.stack.length > 0) {
|
||||
this.current = this.stack[0];
|
||||
this.stack = [];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment
|
||||
* @param content Comment text
|
||||
*/
|
||||
public com(content: string): this {
|
||||
// Comments in fast-xml-parser format
|
||||
if (this.current) {
|
||||
if (!this.current['#comment']) {
|
||||
this.current['#comment'] = [];
|
||||
}
|
||||
if (!Array.isArray(this.current['#comment'])) {
|
||||
this.current['#comment'] = [this.current['#comment']];
|
||||
}
|
||||
this.current['#comment'].push(content);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the XML document
|
||||
* @param options Serialization options
|
||||
*/
|
||||
public end(options?: { prettyPrint?: boolean; format?: string }): string | any {
|
||||
const opts = options || {};
|
||||
|
||||
if (opts.format === 'object') {
|
||||
return this.rootElement;
|
||||
}
|
||||
|
||||
// Use fast-xml-parser to build XML
|
||||
const builder = new plugins.fastXmlParser.XMLBuilder({
|
||||
ignoreAttributes: false,
|
||||
attributeNamePrefix: '@_',
|
||||
format: opts.prettyPrint !== false,
|
||||
indentBy: ' ',
|
||||
});
|
||||
|
||||
const xml = builder.build(this.rootElement || {});
|
||||
return '<?xml version="1.0" encoding="UTF-8"?>\n' + xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to string (alias for end)
|
||||
*/
|
||||
public toString(): string {
|
||||
return this.end() as string;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user