插入文件1中缺少的空XML元素但存在于文件2中

时间:2014-04-25 17:59:26

标签: xml xml-parsing xslt-1.0

我有两个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>

1 个答案:

答案 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>