You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用XPath遍历XML文档时如何避免输出重复节点值

问题根源

你遇到的两个核心问题导致输出不符合预期:

  1. 逻辑重复执行:CorrectingDataBlock属于复杂类型,你代码中获取到它的匹配结果(共2个实例)后,循环2次调用processElement,每次调用都会重新遍历它下面的CurrentVersionData和NewVersionData,相当于叶子节点打印逻辑执行了2次,所以原本4条结果变成了8条。
  2. 输出顺序错误:你每次查询叶子节点都是从XML根节点出发,一次性取所有CorrectingDataBlock下的CurrentVersionData,再一次性取所有NewVersionData,所以输出顺序是所有Current的值→所有New的值,而不是按每个CorrectingDataBlock内部的Current→New的顺序输出。

修复方案

核心修改逻辑:处理可重复的复杂类型节点时,先遍历每个节点实例,再以当前实例为上下文查询它的子节点,而不是每次都从根节点查询所有子节点,同时删掉重复的递归调用逻辑。
修改后的完整代码如下:

export class Parser {

    public _schemeDocument: Document;
    public _dataDocument: Document;

    // 新增contextNode参数,默认从根节点开始查询
    public processElement(element: Element, dataXPath: string, contextNode: Node = this._dataDocument): void {
        const typeName: string = this._getElementTypeName(element);
        const typeElement: Element = this._getTypeElementByName(typeName);

        const currentXPath = dataXPath + `/*[local-name()='${element.getAttribute("name")}']`;
        
        // 先拿到当前层级的所有节点实例
        const currentSnapshot: XPathResult = this._dataDocument.evaluate(currentXPath, contextNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

        if (typeElement && this._isComplexType(typeElement)) {
            const sequence = this._schemeDocument.evaluate("./*[local-name()='sequence']", typeElement).iterateNext();
            if (sequence) {
                // 遍历当前层级的每个节点实例,分别处理子元素,保证顺序
                for (let i = 0; i < currentSnapshot.snapshotLength; i++) {
                    const currentInstance = currentSnapshot.snapshotItem(i) as Element;
                    Array.prototype.forEach.call((sequence as Element).children, (childElement: Element) => {
                        if (this._isElement(childElement)) {
                            const childTypeName: string = this._getElementTypeName(childElement);
                            const childTypeElement: Element = this._getTypeElementByName(childTypeName);
                            
                            if (childTypeElement && this._isComplexType(childTypeElement)) {
                                // 复杂类型递归处理,传入当前实例作为上下文,路径传当前层级的XPath
                                this.processElement(childElement, currentXPath, currentInstance);
                            } else {
                                // 叶子节点,以当前实例为上下文查询,只会拿到当前实例下的子节点值
                                const childXPath = `./*[local-name()='${childElement.getAttribute("name")}']`;
                                const childResult = this._dataDocument.evaluate(childXPath, currentInstance, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                                const childElementCaption: string = this._getElementCaption(childElement);
                                
                                for (let j = 0; j < childResult.snapshotLength; j++) {
                                    const node = childResult.snapshotItem(j) as Element;
                                    const childElementValue = node ? node.textContent : "EMPTY";
                                    console.log(childElementValue);
                                }
                            }
                        }
                    });
                }
            }
        }
    }

    private _getElementTypeName(element: Element): string {
        const splittedTypeName: string[] = element.getAttribute("type").split(":");
        return splittedTypeName.length > 1 ? splittedTypeName[1] : splittedTypeName[0];
    }

    private _getTypeElementByName(typeName: string): Element {
        const simpleTypeXPath = `//*[local-name()='simpleType'][@name='${typeName}']`;
        const complexTypeXPath = `//*[local-name()='complexType'][@name='${typeName}']`;
        return this._schemeDocument.evaluate(`${simpleTypeXPath}|${complexTypeXPath}`, this._schemeDocument).iterateNext() as Element;
    }

    private _getElementCaption(element: Element): string {
        const elementCaption: Node = this._schemeDocument.evaluate(".//*[local-name()='documentation']", element).iterateNext();
        return elementCaption ? elementCaption.textContent : "EMPTY";
    }

    private _isComplexType(element: Element): boolean {
        return element.localName === "complexType";
    }

    private _isElement(element: Element): boolean {
        return element.localName === "element";
    }
}

调用时传入XSD根元素和空路径即可:

// 假设rootXsdElement是XSD里的request元素
parser.processElement(rootXsdElement, "");

修改后输出结果完全符合预期:

current
new
100
200

内容的提问来源于stack exchange,提问作者Artur Vakhitov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:45:03