diff --git a/spec/stopNodes_spec.js b/spec/stopNodes_spec.js
index 939ac90d..221dcbff 100644
--- a/spec/stopNodes_spec.js
+++ b/spec/stopNodes_spec.js
@@ -1,6 +1,7 @@
"use strict";
import { XMLParser, XMLValidator } from "../src/fxp.js";
+import { Expression } from "path-expression-matcher";
describe("XMLParser StopNodes", function () {
it("should support single stopNode with namespace and removeNSPrefix set", function () {
@@ -473,4 +474,58 @@ describe("XMLParser StopNodes", function () {
expect(result).toEqual(expected);
});
+ for (const preserveOrder of [false, true]) {
+ it(`should use stop-node paths in updateTag with preserveOrder=${preserveOrder}`, function () {
+ const paths = [];
+ const parser = new XMLParser({
+ preserveOrder,
+ stopNodes: ["root.skip", "root.keep"],
+ updateTag(tagName, jPath) {
+ paths.push([tagName, jPath]);
+ if (jPath === "root.skip") return false;
+ if (jPath === "root.keep") return "renamed";
+ return tagName;
+ }
+ });
+
+ const result = parser.parse("");
+ const expected = preserveOrder
+ ? [{ root: [{ renamed: [{ "#text": "" }] }, { after: [] }] }]
+ : { root: { renamed: "", after: "" } };
+
+ expect(result).toEqual(expected);
+ expect(paths).toEqual([
+ ["root", "root"],
+ ["skip", "root.skip"],
+ ["keep", "root.keep"],
+ ["after", "root.after"]
+ ]);
+ });
+ }
+
+ for (const item of [' ', '- ']) {
+ it(`should pass the current matcher for an empty stop node: ${item}`, function () {
+ const expression = new Expression("root.item[id=2]");
+ const paths = [];
+ const parser = new XMLParser({
+ ignoreAttributes: false,
+ jPath: false,
+ unpairedTags: ["item"],
+ stopNodes: ["root.item"],
+ updateTag(tagName, matcher, attrs) {
+ paths.push(matcher.toString());
+ if (matcher.matches(expression)) {
+ expect(attrs).toEqual({ "@_id": "2" });
+ return "matched";
+ }
+ return tagName;
+ }
+ });
+
+ expect(parser.parse(`${item}`))
+ .toEqual({ root: { matched: { "@_id": "2" }, after: "" } });
+ expect(paths).toEqual(["root", "root.item", "root.after"]);
+ });
+ }
+
});
diff --git a/src/xmlparser/OrderedObjParser.js b/src/xmlparser/OrderedObjParser.js
index 58c6a5f2..bb2a6308 100644
--- a/src/xmlparser/OrderedObjParser.js
+++ b/src/xmlparser/OrderedObjParser.js
@@ -532,11 +532,12 @@ const parseXml = function (xmlData) {
// For stop nodes, store raw content as-is without any processing
childNode.add(options.textNodeName, tagContent);
+ // Keep the stop node in the matcher until updateTag has been called.
+ this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
+
this.matcher.pop(); // Pop the stop node tag
this.isCurrentNodeStopNode = false; // Reset flag
- this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
-
if (options.captureMetaData) {
currentNode.addEndIndex(i + 1);
}