使用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存在的核心问题
- XPath语法错误:
$file2/[not(@key=$key1)]缺少节点选择器,XPath路径必须指定具体节点(如//node),不能直接用/[条件]。 - 变量作用域错误:全局定义的
$key1/$key2仅能获取根节点的@key,无法动态获取当前遍历节点的key。 - 输出格式不匹配:原输出设置为XML格式,但期望是纯文本,需修改
output的method为text。 - 模板逻辑混乱:匹配所有节点会导致无差别递归,无法精准对比同级节点的差异。
修正后的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" </xsl:text> <!-- 遍历file1的所有node节点,筛选file2中不存在的节点 --> <xsl:apply-templates select=".//node" mode="only-in-file1" /> <xsl:text> "only in file2" </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="', @key, '"')" /> <!-- 如果有value属性则输出 --> <xsl:if test="@value"> <xsl:value-of select="concat(' value="', @value, '"')" /> </xsl:if> <xsl:text> </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="', @key, '"')" /> <xsl:if test="@value"> <xsl:value-of select="concat(' value="', @value, '"')" /> </xsl:if> <xsl:text> </xsl:text> </xsl:if> </xsl:template> <!-- 忽略其他节点(如文本节点) --> <xsl:template match="text()" /> </xsl:stylesheet>
修复说明
- 输出格式调整:将
output的method改为text,直接输出纯文本内容。 - 层级对比逻辑:通过
parent::node/@key确保对比的是同级节点,避免跨层级误判。 - 模板模式区分:使用
mode分别处理"仅在file1"和"仅在file2"的节点,逻辑更清晰。 - 变量动态获取:在每个模板内部定义
$current-key,获取当前节点的@key,保证作用域正确。 - 文本节点忽略:添加匹配
text()的空模板,避免输出XML中的空白换行。
使用说明
- 将修正后的XSLT保存为
transform2.xslt,确保和file1.xml、file2.xml在同一目录。 - 运行原Java代码即可生成符合要求的file3.txt。
内容的提问来源于stack exchange,提问作者pouce
相关产品推荐
相关产品推荐

