英文:
Show differences in a text file between two XML files with XSLT1.0
问题
以下是翻译好的部分:
1°) 检查文件2中是否有新的键和新的值,或者是否已删除键。
2°) 对于每个键,检查值是否已更改。
3°) 在文本文件中指出差异,对于每个差异,指出顶级父节点。
文件1
<node>
<node key="Network1">
<node key="@IP" value="65.12.30.20"/>
<node key="@MAC" value="62-42-85-74-21"/>
<node key="PortNumber" value="12000"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Green"/>
<node key="Office" value="IT"/>
</node>
</node>
<node key="Network2">
<node key="@IP" value="15.58.12.36"/>
<node key="@MAC" value="85-02-14-52-12"/>
<node key="PortNumber" value="12000"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Yellow"/>
<node key="Office" value="IT"/>
</node>
</node>
</node>
文件2
<node>
<node key="Network1">
<node key="@IP" value="65.12.30.20"/>
<node key="@MAC" value="62-42-85-74-21"/>
<node key="PortNumber" value="12001"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Green"/>
<node key="Office" value="HR"/>
<node key="Rack" value="2"/>
</node>
</node>
<node key="Network2">
<node key="@IP" value="15.58.12.36"/>
<node key="@MAC" value="14-85-13-74-36"/>
<node key="PortNumber" value="12001"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Yellow"/>
<node key="Office" value="IT"/>
<node key="Rack" value="1"/>
</node>
</node>
</node>
因此,我想指出差异。
@编辑
使用提供的解决方案,我在此示例中发现了一个错误。
<stackoverflow12>
<node key="Test">
<node key="keyA">
<node key="A" value="A1">
<node key="Name" value="Tim" />
</node>
<node key="A" value="A2">
<node key="Name" value="Tom" />
</node>
</node>
</node>
<node key="Test">
<node key="keyA">
<node key="A" value="A1">
<node key="Name" value="Tim" />
</node>
<node key="A" value="A2">
<node key="Name" value="Tom" />
</node>
</node>
</node>
</stackoverflow12>
这两个XML文件是相同的,但XSLT代码得到了以下结果:
<?xml version="1.0" encoding="UTF-8"?>
<node key="Test">
<node key="keyA">
<node key="A" left-value="A1" right-value="A1"/>
<node key="A" left-value="A2" right-value="A1"/>
</node>
</node>
代码存在问题吗?
英文:
The opening file is file1 which is composed of a lot of network nodes with their children nodes.
Sometimes, the user can change something in the file1, either a value either add a new key and so a new value. The modification are so saved in a new file, file2.
What I would like to do, see the exemple, is :
1°) Check if, in the file2, there is new Keys and so new values or if keys have been deleted.
2°) Check, for each Key, if the value has changed.
3°) Point out the difference in a text file with, for each shift, the top parent node.
File 1
<node>
<node key="Network1">
<node key="@IP" value="65.12.30.20"/>
<node key="@MAC" value="62-42-85-74-21"/>
<node key="PortNumber" value="12000"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Green"/>
<node key="Office" value="IT"/>
</node>
</node>
<node key="Network2">
<node key="@IP" value="15.58.12.36"/>
<node key="@MAC" value="85-02-14-52-12"/>
<node key="PortNumber" value="12000"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Yellow"/>
<node key="Office" value="IT"/>
</node>
</node>
</node>
File 2
<node>
<node key="Network1">
<node key="@IP" value="65.12.30.20"/>
<node key="@MAC" value="62-42-85-74-21"/>
<node key="PortNumber" value="12001"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Green"/>
<node key="Office" value="HR"/>
<node key="Rack" value="2"/>
</node>
</node>
<node key="Network2">
<node key="@IP" value="15.58.12.36"/>
<node key="@MAC" value="14-85-13-74-36"/>
<node key="PortNumber" value="12001"/>
<node key="Location">
<node key="House" value="Blue"/>
<node key="Room" value="Yellow"/>
<node key="Office" value="IT"/>
<node key="Rack" value="1"/>
</node>
</node>
</node>
So, I would like to point out the differences.
@EDIT
With the solution proposed, I've found a bug with this example.
<stackoverflow12>
<node key="Test">
<node key="keyA">
<node key="A" value="A1">
<node key="Name" value="Tim" />
</node>
<node key="A" value="A2">
<node key="Name" value="Tom" />
</node>
</node>
</node>
<node key="Test">
<node key="keyA">
<node key="A" value="A1">
<node key="Name" value="Tim" />
</node>
<node key="A" value="A2">
<node key="Name" value="Tom" />
</node>
</node>
</node>
</stackoverflow12>
The two XML Files are the same but the XSLT code get :
<?xml version="1.0" encoding="UTF-8"?>
<node key="Test">
<node key="keyA">
<node key="A" left-value="A1" right-value="A1"/>
<node key="A" left-value="A2" right-value="A1"/>
</node>
</node>
What's the problem with the code ?
答案1
得分: 2
你需要的输出包括两个文件,它们缺少XML语法所需的闭合标签。建议不直接创建这两个文件,而是建议创建一个"中间"的XML文件,其中包含具有left-value
和right-value
的节点,如果两个输入文件之间的值不同,以及left-node
或right-node
,如果某个键只存在于其中一个文件中。以下样式表会递归地计算这样一个中间XML文件。它会为每个在两个输入文件中相同的具有子节点的节点产生多余的节点,比如<node key="...">
(这在你的示例中并不会发生)。这些可以在第二次遍历时移除,或者在将中间格式转换为你所需的输出时简单地跳过。
样式表期望一个单一的XML输入,将你的两个输入文件合并如下:
<stackoverflow-75629991>
<node>
<!-- File 1 -->
</node>
<node>
<!-- File 2 -->
</node>
</stackoverflow-75629991>
以下是样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<xsl:apply-templates select="node[1]">
<xsl:with-param name="others" select="node[2]" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="node">
<xsl:param name="others" />
<xsl:variable name="other" select="$others[not(@key) or @key=current()/@key]" />
<xsl:choose>
<xsl:when test="@value != $other/@value">
<node key="{@key}" left-value="{@value}" right-value="{$other/@value}" />
</xsl:when>
<xsl:when test="$other and not(@value)">
<node>
<xsl:copy-of select="@key" />
<xsl:apply-templates select="node">
<xsl:with-param name="others" select="$other/node" />
</xsl:apply-templates>
<xsl:for-each select="$other/node[not(@key=current()/node/@key)]">
<right-node>
<xsl:copy-of select="@key | @value | node"/>
</right-node>
</xsl:for-each>
</node>
</xsl:when>
<xsl:when test="not($other)">
<left-node>
<xsl:copy-of select="@key | @value | node"/>
</left-node>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
以下是它生成的中间XML文件的输出:
<node>
<node key="Network1">
<node key="PortNumber" left-value="12000" right-value="12001"/>
<node key="Location">
<node key="Office" left-value="IT" right-value="HR"/>
<right-node key="Rack" value="2"/>
</node>
</node>
<node key="Network2">
<node key="@MAC" left-value="85-02-14-52-12" right-value="14-85-13-74-36"/>
<node key="PortNumber" left-value="12000" right-value="12001"/>
<node key="Location">
<right-node key="Rack" value="1"/>
</node>
</node>
</node>
英文:
Your desired output consists of two files that lack the closing tags demanded by the XML syntax. Rather than create that directly, I suggest creating an "intermediate" XML file that contains nodes with a left-value
and a right-value
if the value differs between the two input files and a left-node
or right-node
if the key exists only in one file. The following stylesheet computes such an intermediate XML file recursively. It would produce superfluous nodes like <node key="...">
for every node with children that is the same in the two input files (which does not occur in your example). These can be removed with a second pass or simply skipped over when the intermediate format is converted into your desired output.
The stylesheet expects a single XML input that combines both your input files like so:
<stackoverflow-75629991>
<node>
<!-- File 1 -->
</node>
<node>
<!-- File 2 -->
</node>
</stackoverflow-75629991>
Here is the stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<xsl:apply-templates select="node[1]">
<xsl:with-param name="others" select="node[2]" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="node">
<xsl:param name="others" />
<xsl:variable name="other" select="$others[not(@key) or @key=current()/@key]" />
<xsl:choose>
<xsl:when test="@value != $other/@value">
<node key="{@key}" left-value="{@value}" right-value="{$other/@value}" />
</xsl:when>
<xsl:when test="$other and not(@value)">
<node>
<xsl:copy-of select="@key" />
<xsl:apply-templates select="node">
<xsl:with-param name="others" select="$other/node" />
</xsl:apply-templates>
<xsl:for-each select="$other/node[not(@key=current()/node/@key)]">
<right-node>
<xsl:copy-of select="@key | @value | node"/>
</right-node>
</xsl:for-each>
</node>
</xsl:when>
<xsl:when test="not($other)">
<left-node>
<xsl:copy-of select="@key | @value | node"/>
</left-node>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
And here is the intermediate XML file it produces as output:
<node>
<node key="Network1">
<node key="PortNumber" left-value="12000" right-value="12001"/>
<node key="Location">
<node key="Office" left-value="IT" right-value="HR"/>
<right-node key="Rack" value="2"/>
</node>
</node>
<node key="Network2">
<node key="@MAC" left-value="85-02-14-52-12" right-value="14-85-13-74-36"/>
<node key="PortNumber" left-value="12000" right-value="12001"/>
<node key="Location">
<right-node key="Rack" value="1"/>
</node>
</node>
</node>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论