我有两个xml文件,我想要做的是在文件1中插入缺少的节点,我只需要带有子节点的元素(如果有的话)。插入的节点应具有与文件2中相同的层次结构。 文件1和2通常具有相同的结构,但在某些情况下,文件1可能在层次结构中具有额外的元素,反之亦然。 当一个元素存在于文件1中而不存在于文件2中时,我想添加一个属性添加一个缺少的属性=" true" to file 1当一个元素在文件2中但在file1中丢失时,它应该插入文件1中相应级别及其所有子元素(我只需要元素而不是它们的内容),其中这些添加的元素将具有复制的属性=& #34;真&#34 ;. 我会使用XSLT 1.0来实现这一点。
以下是一些描述该场景的示例文件。
我们将不胜感激。 这是我到目前为止所拥有的
<?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" indent="yes"/>
<xsl:param name="fra-filename" select="'file2.XML'"/>
<xsl:param name="eng-filename" select="'file1.XML'"/>
<xsl:param name="source-path" select="'C:\'"/>
<xsl:variable name="eng-doc" select="document(concat($source-path, $eng-filename))"/>
<xsl:variable name="fra-doc" select="document(concat($source-path, $fra-filename))"/>
<xsl:template match="RootNode">
<root>
<xsl:apply-templates>
<xsl:with-param name="secFile" select="$fra-doc/RootNode"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="*">
<xsl:param name="element-name" select="local-name()"/>
<xsl:param name="secFile"/>
<xsl:variable name="data1" select="."/>
<xsl:variable name="loc" select="count(preceding-sibling::*[local-name() = $element-name])"/>
<xsl:variable name="data2" select="$secFile/*[local-name() = $element-name][count(preceding-sibling::*[local-name() = $element-name]) = $loc]"/>
<xsl:copy>
<xsl:apply-templates select="node()">
<xsl:with-param name="secFile" select ="$data2"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:if test="not($data2)">
<xsl:element name="{name(.)}">
<xsl:attribute name="missing">
<xsl:text>true</xsl:text>
</xsl:attribute>
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/', name(.))"/>
</xsl:for-each>
<xsl:text> is missing in file 2 </xsl:text>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
输出我得到上面的代码
<?xml version="1.0" encoding="utf-16"?>
<root>
<Comments />
<CreatedBy>author2</CreatedBy>
<CreationDate missing="true">/RootNode/CreationDate is missing in file 2 </CreationDate>
<CreationDate>10/1/2014 01:00:00 PM</CreationDate>
<Details>
<Detail>
<InfoOne>
<Text>1</Text>
</InfoOne>
<InfoTwo>
<Text>Info2</Text>
</InfoTwo>
<InfoThree>
<ExtraInfo missing="true">/RootNode/Details/Detail/InfoThree/ExtraInfo is missing in file 2 </ExtraInfo>
<ExtraInfo />
</InfoThree>
</Detail>
<Detail missing="true">/RootNode/Details/Detail is missing in file 2 </Detail>
<Detail>
<InfoOne missing="true">/RootNode/Details/Detail/InfoOne is missing in file 2 </InfoOne>
<InfoOne>
<Text missing="true">/RootNode/Details/Detail/InfoOne/Text is missing in file 2 </Text>
<Text>5</Text>
</InfoOne>
<InfoTwo missing="true">/RootNode/Details/Detail/InfoTwo is missing in file 2 </InfoTwo>
<InfoTwo>
<Text missing="true">/RootNode/Details/Detail/InfoTwo/Text is missing in file 2 </Text>
<Text>Added information</Text>
</InfoTwo>
<InfoThree missing="true">/RootNode/Details/Detail/InfoThree is missing in file 2 </InfoThree>
<InfoThree>
<Text missing="true">/RootNode/Details/Detail/InfoThree/Text is missing in file 2 </Text>
<Text />
<ExtraInfo missing="true">/RootNode/Details/Detail/InfoThree/ExtraInfo is missing in file 2 </ExtraInfo>
<ExtraInfo />
</InfoThree>
</Detail>
</Details>
</root>
输入文件和所需的输出
File 1
<RootNode>
<Version>3</Version>
<Comments></Comments>
<CreatedBy>author1</CreatedBy>
<Details>
<Detail>
<InfoOne><Text>1</Text></InfoOne>
<InfoTwo wrap="true"><Text>Info from file one</Text></InfoTwo>
<InfoThree><Text></Text></InfoThree>
</Detail>
</Details>
</RootNode>
File 2
<RootNode>
<Comments></Comments>
<CreatedBy>author2</CreatedBy>
<CreationDate checked="true" est="false">10/1/2014 01:00:00 PM</CreationDate>
<Details>
<Detail>
<InfoOne><Text>1</Text></InfoOne>
<InfoTwo wrap="true"><Text>Info2</Text></InfoTwo>
<InfoThree><Text></Text><ExtraInfo wrap="true"></ExtraInfo></InfoThree>
</Detail>
<Detail>
<InfoOne><Text>5</Text></InfoOne>
<InfoTwo wrap="true"><Text>Added information</Text></InfoTwo>
<InfoThree><Text></Text><ExtraInfo wrap="true"></ExtraInfo></InfoThree>
</Detail>
</Details>
</RootNode>
Desired OutPut (File 1)
<RootNode>
<Version missing="true">3</Version>
<Comments></Comments>
<CreatedBy>author1</CreatedBy>
<CreationDate checked="true" est="false" copied="true"></CreationDate>
<Details>
<Detail>
<InfoOne><Text>1</Text></InfoOne>
<InfoTwo wrap="true"><Text>Info from file one</Text></InfoTwo>
<InfoThree><Text></Text><ExtraInfo wrap="true"></ExtraInfo copied="true"></InfoThree>
</Detail>
<Detail copied="true">
<InfoOne copied="true"><Text copied="true"></Text></Info1>
<InfoTwo wrap="true" copied="true"><Text copied="true"></Text></InfoTwo>
<InfoThree copied="true"><Text copied="true"></Text><ExtraInfo wrap="true" copied="true"></ExtraInfo></InfoThree>
</Detail>
</Details>
</RootNode>
答案 0 :(得分:0)
以下XSLT使用for-each
方法而不是优雅的apply-templates
方法。但是,我没有找到任何其他方法来对称地处理这两个文档:我必须遍历第一个文档的两个子项,以便在第二个和中找到缺少的文档,反之亦然。为了实现这一点,我需要一个递归来比较两个子树和一个迭代来比较两个节点列表(cource,也在XSLT 1.0中实现为递归)。
<?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" indent="yes"/>
<xsl:variable name="file2" select="document('file2.xml')"/>
<!-- initialize the recursion by taking the RootNode of the input XML as first sub tree and
the external file file2.xml as second sub tree -->
<xsl:template match="RootNode">
<root>
<xsl:call-template name="compare_trees">
<xsl:with-param name="tree1" select="."/>
<xsl:with-param name="tree2" select="$file2/RootNode"/>
</xsl:call-template>
</root>
</xsl:template>
<xsl:template name="compare_trees">
<xsl:param name="tree1"/>
<xsl:param name="tree2"/>
<!-- initalize the iteration over all the children of the sub trees ... -->
<xsl:call-template name="compare_nodelists">
<xsl:with-param name="nodelist1" select="$tree1/*" />
<xsl:with-param name="nodelist2" select="$tree2/*" />
</xsl:call-template>
</xsl:template>
<xsl:template name="compare_nodelists">
<xsl:param name="nodelist1"/>
<xsl:param name="nodelist2"/>
<xsl:if test="count($nodelist1) > 0 or count($nodelist2) > 0">
<xsl:variable name="element-name1" select="local-name($nodelist1[1])"/>
<xsl:variable name="element-name2" select="local-name($nodelist2[1])"/>
<xsl:variable name="length1" select="count($nodelist1)"/>
<xsl:variable name="length2" select="count($nodelist2)"/>
<xsl:variable name="count1" select="count($nodelist2[local-name() = $element-name1])"/>
<xsl:variable name="count2" select="count($nodelist1[local-name() = $element-name2])"/>
<!-- take a look at the first node of the first node list ... -->
<!-- and try to find a matching node in the second node list ... -->
<xsl:choose>
<xsl:when test="$element-name2 = $element-name1">
<!-- the matching node is found and it's also the first node in the second list-->
<!-- -> create the first node of the first list in the target document ... -->
<xsl:element name="{$element-name1}">
<xsl:copy-of select="$nodelist1[1]/@*"/>
<xsl:copy-of select="normalize-space($nodelist1[1]/text())"/>
<!-- do a recursion into the first nodes of both lists -->
<xsl:call-template name="compare_trees">
<xsl:with-param name="tree1" select="$nodelist1[1]"/>
<xsl:with-param name="tree2" select="$nodelist2[1]"/>
</xsl:call-template>
</xsl:element>
<!-- ... and interate over the remaining nodes -->
<xsl:call-template name="compare_nodelists">
<xsl:with-param name="nodelist1" select="$nodelist1[position() > 1]"/>
<xsl:with-param name="nodelist2" select="$nodelist2[position() > 1]"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$count1 > 0 or $length1 = 0">
<!-- the matching node is found but it's not the first node in the second list or the first list is etpty -->
<!-- this means that the first node of the second list does not exist in the first list -->
<!-- -> create the first node of the second in the target document -->
<xsl:element name="{local-name($nodelist2[1])}">
<xsl:attribute name="copied">
<xsl:text>true</xsl:text>
</xsl:attribute>
<xsl:copy-of select="$nodelist2[1]/@*"/>
<xsl:copy-of select="$nodelist2[1]/*"/>
</xsl:element>
<!-- ... and interate over the remaining nodes -->
<xsl:call-template name="compare_nodelists">
<xsl:with-param name="nodelist1" select="$nodelist1"/>
<xsl:with-param name="nodelist2" select="$nodelist2[position() > 1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- now we know there's no matching node in the second node list -->
<!-- move the attention to the first node of the second node list -->
<!-- and try to find a matching node in the first node list ... -->
<xsl:choose>
<xsl:when test="$count2 > 0 or $length2 = 0">
<!-- the matching node is found but it's not the first node in the first list or the second list is empty -->
<!-- -> create the first node of the first list in the target document ... -->
<xsl:element name="{local-name($nodelist1[1])}">
<xsl:attribute name="missing">
<xsl:text>true</xsl:text>
</xsl:attribute>
<xsl:copy-of select="$nodelist1[1]/@*"/>
<xsl:copy-of select="$nodelist1[1]/text()"/>
<xsl:copy-of select="$nodelist1[1]/*"/>
</xsl:element>
<!-- ... and iterate over the remaining nodes -->
<xsl:call-template name="compare_nodelists">
<xsl:with-param name="nodelist1" select="$nodelist1[position() > 1]"/>
<xsl:with-param name="nodelist2" select="$nodelist2"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message>ERROR. We should never end up here!</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>