fast-xml-parser 5.4.2 → 5.5.1

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.
@@ -1,35 +1,66 @@
1
1
  'use strict';
2
2
 
3
3
  import XmlNode from './xmlNode.js';
4
+ import { Matcher } from 'path-expression-matcher';
4
5
 
5
6
  const METADATA_SYMBOL = XmlNode.getMetaDataSymbol();
6
7
 
8
+ /**
9
+ * Helper function to strip attribute prefix from attribute map
10
+ * @param {object} attrs - Attributes with prefix (e.g., {"@_class": "code"})
11
+ * @param {string} prefix - Attribute prefix to remove (e.g., "@_")
12
+ * @returns {object} Attributes without prefix (e.g., {"class": "code"})
13
+ */
14
+ function stripAttributePrefix(attrs, prefix) {
15
+ if (!attrs || typeof attrs !== 'object') return {};
16
+ if (!prefix) return attrs;
17
+
18
+ const rawAttrs = {};
19
+ for (const key in attrs) {
20
+ if (key.startsWith(prefix)) {
21
+ const rawName = key.substring(prefix.length);
22
+ rawAttrs[rawName] = attrs[key];
23
+ } else {
24
+ // Attribute without prefix (shouldn't normally happen, but be safe)
25
+ rawAttrs[key] = attrs[key];
26
+ }
27
+ }
28
+ return rawAttrs;
29
+ }
30
+
7
31
  /**
8
32
  *
9
33
  * @param {array} node
10
34
  * @param {any} options
35
+ * @param {Matcher} matcher - Path matcher instance
11
36
  * @returns
12
37
  */
13
- export default function prettify(node, options) {
14
- return compress(node, options);
38
+ export default function prettify(node, options, matcher) {
39
+ return compress(node, options, matcher);
15
40
  }
16
41
 
17
42
  /**
18
43
  *
19
44
  * @param {array} arr
20
45
  * @param {object} options
21
- * @param {string} jPath
46
+ * @param {Matcher} matcher - Path matcher instance
22
47
  * @returns object
23
48
  */
24
- function compress(arr, options, jPath) {
49
+ function compress(arr, options, matcher) {
25
50
  let text;
26
51
  const compressedObj = {}; //This is intended to be a plain object
27
52
  for (let i = 0; i < arr.length; i++) {
28
53
  const tagObj = arr[i];
29
54
  const property = propName(tagObj);
30
- let newJpath = "";
31
- if (jPath === undefined) newJpath = property;
32
- else newJpath = jPath + "." + property;
55
+
56
+ // Push current property to matcher WITH RAW ATTRIBUTES (no prefix)
57
+ if (property !== undefined && property !== options.textNodeName) {
58
+ const rawAttrs = stripAttributePrefix(
59
+ tagObj[":@"] || {},
60
+ options.attributeNamePrefix
61
+ );
62
+ matcher.push(property, rawAttrs);
63
+ }
33
64
 
34
65
  if (property === options.textNodeName) {
35
66
  if (text === undefined) text = tagObj[property];
@@ -38,11 +69,11 @@ function compress(arr, options, jPath) {
38
69
  continue;
39
70
  } else if (tagObj[property]) {
40
71
 
41
- let val = compress(tagObj[property], options, newJpath);
72
+ let val = compress(tagObj[property], options, matcher);
42
73
  const isLeaf = isLeafTag(val, options);
43
74
 
44
75
  if (tagObj[":@"]) {
45
- assignAttributes(val, tagObj[":@"], newJpath, options);
76
+ assignAttributes(val, tagObj[":@"], matcher, options);
46
77
  } else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) {
47
78
  val = val[options.textNodeName];
48
79
  } else if (Object.keys(val).length === 0) {
@@ -63,12 +94,20 @@ function compress(arr, options, jPath) {
63
94
  } else {
64
95
  //TODO: if a node is not an array, then check if it should be an array
65
96
  //also determine if it is a leaf node
66
- if (options.isArray(property, newJpath, isLeaf)) {
97
+
98
+ // Pass jPath string or matcher based on options.jPath setting
99
+ const jPathOrMatcher = options.jPath ? matcher.toString() : matcher;
100
+ if (options.isArray(property, jPathOrMatcher, isLeaf)) {
67
101
  compressedObj[property] = [val];
68
102
  } else {
69
103
  compressedObj[property] = val;
70
104
  }
71
105
  }
106
+
107
+ // Pop property from matcher after processing
108
+ if (property !== undefined && property !== options.textNodeName) {
109
+ matcher.pop();
110
+ }
72
111
  }
73
112
 
74
113
  }
@@ -89,13 +128,25 @@ function propName(obj) {
89
128
  }
90
129
  }
91
130
 
92
- function assignAttributes(obj, attrMap, jpath, options) {
131
+ function assignAttributes(obj, attrMap, matcher, options) {
93
132
  if (attrMap) {
94
133
  const keys = Object.keys(attrMap);
95
134
  const len = keys.length; //don't make it inline
96
135
  for (let i = 0; i < len; i++) {
97
- const atrrName = keys[i];
98
- if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
136
+ const atrrName = keys[i]; // This is the PREFIXED name (e.g., "@_class")
137
+
138
+ // Strip prefix for matcher path (for isArray callback)
139
+ const rawAttrName = atrrName.startsWith(options.attributeNamePrefix)
140
+ ? atrrName.substring(options.attributeNamePrefix.length)
141
+ : atrrName;
142
+
143
+ // For attributes, we need to create a temporary path
144
+ // Pass jPath string or matcher based on options.jPath setting
145
+ const jPathOrMatcher = options.jPath
146
+ ? matcher.toString() + "." + rawAttrName
147
+ : matcher;
148
+
149
+ if (options.isArray(atrrName, jPathOrMatcher, true, true)) {
99
150
  obj[atrrName] = [attrMap[atrrName]];
100
151
  } else {
101
152
  obj[atrrName] = attrMap[atrrName];
@@ -120,4 +171,4 @@ function isLeafTag(obj, options) {
120
171
  }
121
172
 
122
173
  return false;
123
- }
174
+ }