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

使用XSLT对比两个XML文件的语法错误解决及差异输出需求

XML文件对比XSLT错误修复方案

问题背景

现有两个XML文件file1.xml和file2.xml,需通过XSLT对比内容并将差异输出到file3.txt,但原XSLT中的$file2/[not(@key=$key1)]存在语法错误,无法正常运行。以下是相关文件内容及期望输出:

File1.xml

<node key="U">
  <node key="E" value="E1"/>
  <node key="B" value="B1"/>
  <node key="Z" value="Z1"/>
  <node key="T">
    <node key="Y">
      <node key="H" value="H1"/>
      <node key="C" value="C1"/>
      <node key="P" value="P1"/>
    </node>
  </node>
</node>

File2.xml

<node key="U">
  <node key="E" value="E1"/>
  <node key="B" value="O1"/>
  <node key="G" value="Z1"/>
  <node key="T">
    <node key="Y2">
      <node key="H" value="H1"/>
      <node key="C1" value="C8"/>
      <node key="P" value="P1"/>
    </node>
  </node>
</node>

原错误XSLT代码

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"  omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:variable name="file2" select="document('file2.xml')" />

<xsl:variable name="key1" select="@key" />
<xsl:variable name="key2" select="@key" />

<xsl:template match="@* | node()">
    <xsl:comment>"only in file1"</xsl:comment>
        <xsl:apply-templates select="[not(@key=$key2)]"/>
    <xsl:comment>"only in file2"</xsl:comment>
        <xsl:apply-templates select="$file2/[not(@key=$key1)]"/>
</xsl:template>
</xsl:stylesheet>

Java调用代码

package XSLT;

import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;

public class MainCompare {
    public static void main(String[] args) throws IOException, URISyntaxException, TransformerException {
        TransformerFactory factory = TransformerFactory.newInstance();
        Source xslt = new StreamSource(new File("transform2.xslt"));
        Transformer transformer = factory.newTransformer(xslt);

        Source text = new StreamSource(new File("file1.xml"));
        transformer.transform(text, new StreamResult(new File("file3.txt")));
    }
}

期望输出

"only in file1"
 
  key="B" value="B1"
  key="Z" value="Z1"
  key="Y"
  key="C" value="C1"
 
 "only in file2"
 
   key="B" value="O1"
   key="G" value="Z1"
   key="Y2"
   key="C1" value="C8"

错误分析与修复

原XSLT存在的核心问题

  1. XPath语法错误:$file2/[not(@key=$key1)]缺少节点选择器,XPath路径必须指定具体节点(如//node),不能直接用/[条件]。
  2. 变量作用域错误:全局定义的$key1/$key2仅能获取根节点的@key,无法动态获取当前遍历节点的key。
  3. 输出格式不匹配:原输出设置为XML格式,但期望是纯文本,需修改output的method为text。
  4. 模板逻辑混乱:匹配所有节点会导致无差别递归,无法精准对比同级节点的差异。

修正后的XSLT代码

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- 输出纯文本格式 -->
    <xsl:output method="text" encoding="utf-8" />

    <!-- 加载第二个XML文件 -->
    <xsl:variable name="file2" select="document('file2.xml')" />

    <!-- 根节点模板:触发对比逻辑 -->
    <xsl:template match="/node">
        <xsl:text> "only in file1"&#10;&#10;</xsl:text>
        <!-- 遍历file1的所有node节点,筛选file2中不存在的节点 -->
        <xsl:apply-templates select=".//node" mode="only-in-file1" />
        
        <xsl:text>&#10; "only in file2"&#10;&#10;</xsl:text>
        <!-- 遍历file2的所有node节点,筛选file1中不存在的节点 -->
        <xsl:apply-templates select="$file2//node" mode="only-in-file2" />
    </xsl:template>

    <!-- 仅在file1存在的节点模板 -->
    <xsl:template match="node" mode="only-in-file1">
        <xsl:variable name="current-key" select="@key" />
        <!-- 检查file2中是否存在同层级同key的节点 -->
        <xsl:if test="count($file2//node[parent::node/@key = current()/parent::node/@key][@key = $current-key]) = 0">
            <xsl:text>  </xsl:text>
            <xsl:value-of select="concat('key=&quot;', @key, '&quot;')" />
            <!-- 如果有value属性则输出 -->
            <xsl:if test="@value">
                <xsl:value-of select="concat(' value=&quot;', @value, '&quot;')" />
            </xsl:if>
            <xsl:text>&#10;</xsl:text>
        </xsl:if>
    </xsl:template>

    <!-- 仅在file2存在的节点模板 -->
    <xsl:template match="node" mode="only-in-file2">
        <xsl:variable name="current-key" select="@key" />
        <!-- 检查file1中是否存在同层级同key的节点 -->
        <xsl:if test="count(//node[parent::node/@key = current()/parent::node/@key][@key = $current-key]) = 0">
            <xsl:text>   </xsl:text>
            <xsl:value-of select="concat('key=&quot;', @key, '&quot;')" />
            <xsl:if test="@value">
                <xsl:value-of select="concat(' value=&quot;', @value, '&quot;')" />
            </xsl:if>
            <xsl:text>&#10;</xsl:text>
        </xsl:if>
    </xsl:template>

    <!-- 忽略其他节点(如文本节点) -->
    <xsl:template match="text()" />
</xsl:stylesheet>

修复说明

  1. 输出格式调整:将output的method改为text,直接输出纯文本内容。
  2. 层级对比逻辑:通过parent::node/@key确保对比的是同级节点,避免跨层级误判。
  3. 模板模式区分:使用mode分别处理"仅在file1"和"仅在file2"的节点,逻辑更清晰。
  4. 变量动态获取:在每个模板内部定义$current-key,获取当前节点的@key,保证作用域正确。
  5. 文本节点忽略:添加匹配text()的空模板,避免输出XML中的空白换行。

使用说明

  1. 将修正后的XSLT保存为transform2.xslt,确保和file1.xml、file2.xml在同一目录。
  2. 运行原Java代码即可生成符合要求的file3.txt。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 20:27:18