xslt-processor 4.2.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -89,12 +89,57 @@ const xslt = new Xslt(options);
89
89
  - `cData` (`boolean`, default `true`): resolves CDATA elements in the output. Content under CDATA is resolved as text. This overrides `escape` for CDATA content.
90
90
  - `escape` (`boolean`, default `true`): replaces symbols like `<`, `>`, `&` and `"` by the corresponding [HTML/XML entities](https://www.tutorialspoint.com/xml/xml_character_entities.htm). Can be overridden by `disable-output-escaping`, that also does the opposite, unescaping `&gt;` and `&lt;` by `<` and `>`, respectively.
91
91
  - `selfClosingTags` (`boolean`, default `true`): Self-closes tags that don't have inner elements, if `true`. For instance, `<test></test>` becomes `<test />`.
92
- - `outputMethod` (`string`, default `xml`): Specifies the default output method. if `<xsl:output>` is declared in your XSLT file, this will be overridden. Valid values: `xml`, `html`, `text`, `name`, `xhtml`.
92
+ - `outputMethod` (`string`, default `xml`): Specifies the default output method. if `<xsl:output>` is declared in your XSLT file, this will be overridden. Valid values: `xml`, `html`, `text`, `name`, `xhtml`, `json`.
93
93
  - `parameters` (`array`, default `[]`): external parameters that you want to use.
94
94
  - `name`: the parameter name;
95
95
  - `namespaceUri` (optional): the namespace;
96
96
  - `value`: the value.
97
97
 
98
+ #### JSON Output Format
99
+
100
+ When using `outputMethod: 'json'`, the XSLT processor will convert the resulting XML document to JSON format. This is useful for APIs and modern JavaScript applications.
101
+
102
+ **Example:**
103
+
104
+ ```js
105
+ const xslt = new Xslt({ outputMethod: 'json' });
106
+ const xmlParser = new XmlParser();
107
+
108
+ const xmlString = `<root>
109
+ <users>
110
+ <user>Alice</user>
111
+ <user>Bob</user>
112
+ </users>
113
+ </root>`;
114
+
115
+ const xsltString = `<?xml version="1.0"?>
116
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
117
+ <xsl:template match="/">
118
+ <xsl:copy-of select="root"/>
119
+ </xsl:template>
120
+ </xsl:stylesheet>`;
121
+
122
+ const result = await xslt.xsltProcess(
123
+ xmlParser.xmlParse(xmlString),
124
+ xmlParser.xmlParse(xsltString)
125
+ );
126
+
127
+ // result will be a JSON string:
128
+ // {"root":{"users":{"user":["Alice","Bob"]}}}
129
+
130
+ const parsed = JSON.parse(result);
131
+ console.log(parsed.root.users.user); // ["Alice", "Bob"]
132
+ ```
133
+
134
+ **JSON Structure Rules:**
135
+
136
+ - Each element becomes a property in a JSON object
137
+ - Text-only elements become string values
138
+ - Elements with multiple children of the same name become arrays
139
+ - Empty elements are omitted from the output
140
+ - Attributes are prefixed with `@` (when present in the output)
141
+ - Mixed text and element content uses the `#text` property for text nodes
142
+
98
143
  ### Direct use in browsers
99
144
 
100
145
  You can simply add a tag like this:
package/index.d.mts CHANGED
@@ -532,6 +532,7 @@ type XsltOptions = {
532
532
  cData: boolean;
533
533
  escape: boolean;
534
534
  selfClosingTags: boolean;
535
+ outputMethod?: 'xml' | 'html' | 'text' | 'xhtml' | 'json';
535
536
  parameters?: XsltParameter[];
536
537
  };
537
538
 
@@ -566,7 +567,7 @@ declare class Xslt {
566
567
  options: XsltOptions;
567
568
  decimalFormatSettings: XsltDecimalFormatSettings;
568
569
  outputDocument: XDocument;
569
- outputMethod: 'xml' | 'html' | 'text' | 'name' | 'xhtml';
570
+ outputMethod: 'xml' | 'html' | 'text' | 'name' | 'xhtml' | 'json';
570
571
  outputOmitXmlDeclaration: string;
571
572
  version: string;
572
573
  firstTemplateRan: boolean;
@@ -591,7 +592,7 @@ declare class Xslt {
591
592
  * The exported entry point of the XSL-T processor.
592
593
  * @param xmlDoc The input document root, as DOM node.
593
594
  * @param stylesheet The stylesheet document root, as DOM node.
594
- * @returns the processed document, as XML text in a string.
595
+ * @returns the processed document, as XML text in a string, or JSON string if outputMethod is 'json'.
595
596
  */
596
597
  xsltProcess(xmlDoc: XDocument, stylesheet: XDocument): Promise<string>;
597
598
  /**
package/index.d.ts CHANGED
@@ -532,6 +532,7 @@ type XsltOptions = {
532
532
  cData: boolean;
533
533
  escape: boolean;
534
534
  selfClosingTags: boolean;
535
+ outputMethod?: 'xml' | 'html' | 'text' | 'xhtml' | 'json';
535
536
  parameters?: XsltParameter[];
536
537
  };
537
538
 
@@ -566,7 +567,7 @@ declare class Xslt {
566
567
  options: XsltOptions;
567
568
  decimalFormatSettings: XsltDecimalFormatSettings;
568
569
  outputDocument: XDocument;
569
- outputMethod: 'xml' | 'html' | 'text' | 'name' | 'xhtml';
570
+ outputMethod: 'xml' | 'html' | 'text' | 'name' | 'xhtml' | 'json';
570
571
  outputOmitXmlDeclaration: string;
571
572
  version: string;
572
573
  firstTemplateRan: boolean;
@@ -591,7 +592,7 @@ declare class Xslt {
591
592
  * The exported entry point of the XSL-T processor.
592
593
  * @param xmlDoc The input document root, as DOM node.
593
594
  * @param stylesheet The stylesheet document root, as DOM node.
594
- * @returns the processed document, as XML text in a string.
595
+ * @returns the processed document, as XML text in a string, or JSON string if outputMethod is 'json'.
595
596
  */
596
597
  xsltProcess(xmlDoc: XDocument, stylesheet: XDocument): Promise<string>;
597
598
  /**
package/index.js CHANGED
@@ -2213,6 +2213,122 @@ function xmlGetAttribute(node, name) {
2213
2213
  }
2214
2214
  return value;
2215
2215
  }
2216
+ function nodeToJsonObject(node) {
2217
+ if (!node) {
2218
+ return null;
2219
+ }
2220
+ const nodeType = node.nodeType;
2221
+ if (nodeType === DOM_TEXT_NODE || nodeType === DOM_CDATA_SECTION_NODE) {
2222
+ const text = node.nodeValue ? node.nodeValue.trim() : "";
2223
+ return text.length > 0 ? text : null;
2224
+ }
2225
+ if (nodeType === DOM_COMMENT_NODE) {
2226
+ return null;
2227
+ }
2228
+ if (nodeType === DOM_DOCUMENT_NODE || nodeType === DOM_DOCUMENT_FRAGMENT_NODE) {
2229
+ const children = node.childNodes || [];
2230
+ const childObjects = [];
2231
+ for (let i = 0; i < children.length; i++) {
2232
+ const child = children[i];
2233
+ const childObj = nodeToJsonObject(child);
2234
+ if (childObj !== null) {
2235
+ childObjects.push(childObj);
2236
+ }
2237
+ }
2238
+ if (childObjects.length === 0) {
2239
+ return null;
2240
+ } else if (childObjects.length === 1) {
2241
+ return childObjects[0];
2242
+ } else {
2243
+ return childObjects;
2244
+ }
2245
+ }
2246
+ if (nodeType === DOM_ELEMENT_NODE) {
2247
+ const obj = {};
2248
+ const element = node;
2249
+ const hasAttributes = element.attributes && element.attributes.length > 0;
2250
+ if (hasAttributes) {
2251
+ for (let i = 0; i < element.attributes.length; i++) {
2252
+ const attr = element.attributes[i];
2253
+ obj["@" + attr.nodeName] = attr.nodeValue;
2254
+ }
2255
+ }
2256
+ const children = element.childNodes || [];
2257
+ let textContent = "";
2258
+ let hasElementChildren = false;
2259
+ const childElements = {};
2260
+ for (let i = 0; i < children.length; i++) {
2261
+ const child = children[i];
2262
+ const childType = child.nodeType;
2263
+ if (childType === DOM_TEXT_NODE || childType === DOM_CDATA_SECTION_NODE) {
2264
+ const text = child.nodeValue ? child.nodeValue.trim() : "";
2265
+ if (text.length > 0) {
2266
+ textContent += text;
2267
+ }
2268
+ } else if (childType === DOM_ELEMENT_NODE) {
2269
+ hasElementChildren = true;
2270
+ const childElement = child;
2271
+ const childName = childElement.localName || childElement.nodeName;
2272
+ const childObj = nodeToJsonObject(child);
2273
+ if (childObj !== null) {
2274
+ if (childElements[childName]) {
2275
+ if (!Array.isArray(childElements[childName])) {
2276
+ childElements[childName] = [childElements[childName]];
2277
+ }
2278
+ childElements[childName].push(childObj);
2279
+ } else {
2280
+ childElements[childName] = childObj;
2281
+ }
2282
+ }
2283
+ }
2284
+ }
2285
+ Object.assign(obj, childElements);
2286
+ if (!hasElementChildren && textContent.length > 0) {
2287
+ if (!hasAttributes && Object.keys(childElements).length === 0) {
2288
+ return textContent;
2289
+ } else {
2290
+ obj["#text"] = textContent;
2291
+ }
2292
+ }
2293
+ if (Object.keys(obj).length === 0) {
2294
+ return null;
2295
+ }
2296
+ return obj;
2297
+ }
2298
+ return null;
2299
+ }
2300
+ function xmlToJson(node) {
2301
+ if (!node) {
2302
+ return "{}";
2303
+ }
2304
+ let rootElement = node;
2305
+ if (node.nodeType === DOM_DOCUMENT_NODE || node.nodeType === DOM_DOCUMENT_FRAGMENT_NODE) {
2306
+ const children = node.childNodes || [];
2307
+ for (let i = 0; i < children.length; i++) {
2308
+ if (children[i].nodeType === DOM_ELEMENT_NODE) {
2309
+ rootElement = children[i];
2310
+ break;
2311
+ }
2312
+ }
2313
+ }
2314
+ const element = rootElement;
2315
+ const rootName = element.localName || element.nodeName;
2316
+ const jsonObj = {};
2317
+ const elementContent = nodeToJsonObject(rootElement);
2318
+ if (elementContent === null) {
2319
+ jsonObj[rootName] = {};
2320
+ } else if (typeof elementContent === "object" && !Array.isArray(elementContent)) {
2321
+ jsonObj[rootName] = elementContent;
2322
+ } else {
2323
+ jsonObj[rootName] = elementContent;
2324
+ }
2325
+ try {
2326
+ const cleaned = JSON.parse(JSON.stringify(jsonObj));
2327
+ return JSON.stringify(cleaned);
2328
+ } catch (error) {
2329
+ return JSON.stringify(jsonObj);
2330
+ }
2331
+ }
2216
2332
 
2217
2333
  // src/dom/xml-parser.ts
2218
2334
  var import_he2 = __toESM(require("he"));
@@ -3551,9 +3667,10 @@ var Xslt = class {
3551
3667
  cData: options.cData === true,
3552
3668
  escape: options.escape === true,
3553
3669
  selfClosingTags: options.selfClosingTags === true,
3670
+ outputMethod: options.outputMethod,
3554
3671
  parameters: options.parameters || []
3555
3672
  };
3556
- this.outputMethod = "xml";
3673
+ this.outputMethod = options.outputMethod || "xml";
3557
3674
  this.outputOmitXmlDeclaration = "no";
3558
3675
  this.stripSpacePatterns = [];
3559
3676
  this.preserveSpacePatterns = [];
@@ -3576,7 +3693,7 @@ var Xslt = class {
3576
3693
  * The exported entry point of the XSL-T processor.
3577
3694
  * @param xmlDoc The input document root, as DOM node.
3578
3695
  * @param stylesheet The stylesheet document root, as DOM node.
3579
- * @returns the processed document, as XML text in a string.
3696
+ * @returns the processed document, as XML text in a string, or JSON string if outputMethod is 'json'.
3580
3697
  */
3581
3698
  xsltProcess(xmlDoc, stylesheet) {
3582
3699
  return __async(this, null, function* () {
@@ -3589,6 +3706,9 @@ var Xslt = class {
3589
3706
  }
3590
3707
  }
3591
3708
  yield this.xsltProcessContext(expressionContext, stylesheet, this.outputDocument);
3709
+ if (this.outputMethod === "json") {
3710
+ return xmlToJson(outputDocument);
3711
+ }
3592
3712
  const transformedOutputXml = xmlTransformedText(outputDocument, {
3593
3713
  cData: this.options.cData,
3594
3714
  escape: this.options.escape,