找到匹配的标记时复制XML值

时间:2013-04-04 21:52:49

标签: xml xslt copy

首先在这里发帖,找不到我需要的东西。 我有2个XML文档,它们具有相似但略有不同的结构,并且标签中可能有不同的值。 我需要遍历一个XML中的所有叶节点,如果相同的标记(仅通过完整的XPath位置)位于另一个XML中,我需要将值复制到目标XML。 我需要自动执行此操作。不使用属性。

例如,我的基地:

<root>
 <a>abc</a>
 <b>def</b>
</root>

目标:

<root>
 <a>xyz</a>
 <c>ghi</c>
</root>

预期产出:

<root>
 <a>abc</a>
 <c>ghi</c>
</root>

使用XSLT和简单的Linux工具/ shell脚本比第三方工具更受欢迎。 谢谢!

2 个答案:

答案 0 :(得分:0)

XSLT 3.0 样式表为没有任何子元素的元素生成动态XPath字符串,并使用<xsl:evaluate>从“基本”XML文件中选择匹配元素(命名为'base.xml'和该示例的XML输入在同一目录中。

如果“base”中有匹配的元素,它会使用该元素的文本,否则会复制匹配元素的文本。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="my"
    exclude-result-prefixes="xs"
    version="3.0">
    <xsl:output indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>        
    </xsl:template>

<!--Elements that do not have any child elements -->
    <xsl:template match="*[not(*)]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
<!--select the equivalent element from the "base" XML file (if any), 
    using the XPath generated from the my:baseMatchXPath() function --> 
            <xsl:variable name="baseMatch" as="item()*">
                <xsl:evaluate xpath="my:baseMatchXPath(.)"  />    
            </xsl:variable>
<!--
Create a sequence of nodes using the $baseMatch and the currently matched element. 
Use a predicate filter to select the first one 
  (if there is no baseMatch, then the context node is the first).
Apply templates to that element's child node()'s. 

NOTE: In this example it is the text() node, 
      but this would also preserve any comments or processing instructions
-->
            <xsl:apply-templates select="($baseMatch, .)[1]/node()"/>   
        </xsl:copy>
    </xsl:template>

    <xsl:function name="my:baseMatchXPath" as="xs:string">
        <xsl:param name="contextNode"/>
        <xsl:variable name="base">document('base.xml')</xsl:variable>
        <xsl:sequence select="concat($base, 
                                     string-join(my:ancestors($contextNode), ''))"/>
    </xsl:function>

    <xsl:function name="my:ancestors" as="item()*">
      <xsl:param name="contextNode"/>

      <xsl:if test="$contextNode/ancestor::*[1]">
          <xsl:sequence select="my:ancestors($contextNode/ancestor::*[1])"/>
      </xsl:if>
      <xsl:sequence select="'/'"/>
      <xsl:sequence select="name($contextNode)"/>
      <xsl:if test="count($contextNode/parent::*/*[name()=name($contextNode)]) 
                      > 1">
          <xsl:sequence 
               select="concat(
                         '[', 
                         count($contextNode/preceding-sibling::*[name()=name($contextNode)])+1, 
                         ']')"/>
      </xsl:if>            
    </xsl:function>

</xsl:stylesheet>

答案 1 :(得分:0)

这是一个XSLT 1.0解决方案 - 我们只假设这两个文档没有混合内容元素(没有元素同时包含元素和非空白文本节点子节点):

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

     <my:patternDoc>
        <root>
         <a>abc</a>
         <b>def</b>
        </root>
     </my:patternDoc>

 <xsl:variable name="vinitTarget" select="/"/>
 <xsl:variable name="vinitPattern" select="document('')/*/my:patternDoc"/>

 <xsl:template match="/*">
  <xsl:copy>
   <xsl:call-template name="clone"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template name="clone">
  <xsl:param name="pTargetNode" select="/*/*[1]"/>
  <xsl:param name="pPatterntNode" select="$vinitPattern/*/*[1]"/>

  <xsl:if test="$pTargetNode">
  <xsl:choose>
    <xsl:when test="not(name($pTargetNode) = name($pPatterntNode))">
      <xsl:copy-of select="$pTargetNode"/>

      <xsl:call-template name="clone">
        <xsl:with-param name="pTargetNode"
          select="$pTargetNode/following-sibling::*[1]"/>
        <xsl:with-param name="pPatterntNode"
          select="$pPatterntNode/following-sibling::*[1]"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
     <xsl:choose>
       <xsl:when test="$pTargetNode[not(*)] and $pPatterntNode[not(*)]">
         <xsl:copy-of select="$pPatterntNode"/>
             <xsl:call-template name="clone">
                <xsl:with-param name="pTargetNode"
                  select="$pTargetNode/following-sibling::*[1]"/>
                <xsl:with-param name="pPatterntNode"
                  select="$pPatterntNode/following-sibling::*[1]"/>
             </xsl:call-template>
       </xsl:when>
       <xsl:when test="$pTargetNode/*">
             <xsl:element name="{name($pTargetNode)}"
                  namespace="{namespace-uri($pTargetNode)}">
                 <xsl:call-template name="clone">
                    <xsl:with-param name="pTargetNode"
                      select="$pTargetNode/*[1]"/>
                    <xsl:with-param name="pPatterntNode"
                      select="$pPatterntNode/*[1]"/>
                 </xsl:call-template>
             </xsl:element>
           <xsl:call-template name="clone">
            <xsl:with-param name="pTargetNode"
              select="$pTargetNode/following-sibling::*[1]"/>
            <xsl:with-param name="pPatterntNode"
              select="$pPatterntNode/following-sibling::*[1]"/>
           </xsl:call-template>
       </xsl:when>
     </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

在提供的“目标”XML文档上应用此转换时:

<root>
 <a>xyz</a>
 <c>ghi</c>
</root>

产生了想要的正确结果:

<root>
   <a xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">abc</a>
   <c>ghi</c>
</root>

注意

复制的额外命名空间仅仅是因为模式文档当前嵌入到XSLT样式表中。这只是为了方便。在实际情况中,模式文档将与XSLT样式表分开,并且不会复制额外的命名空间节点。

让我们验证提供的转换是否可以在更复杂的XML文档上正常运行:

<root>
    <a>
        <b>
            <c>xyz</c>
            <f/>
            <g>tuv</g>
        </b>
    </a>
    <d>
        <e>ghi</e>
    </d>
</root>

如果嵌入式模式文档现在在同一转换中(上图):

<my:patternDoc>
    <root>
        <a>
            <b>
             <c>abc</c>
         <f/>
         <g>mnk</g>
         </b>
        </a>
        <d>
            <e>pqr</e>
        </d>
     </root>
  </my:patternDoc>

再次生成想要的正确结果:

<root>
   <a>
      <b>
         <c xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">abc</c>
         <f xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my"/>
         <g xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">mnk</g>
      </b>
   </a>
   <d>
      <e xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">pqr</e>
   </d>
</root>