【问题标题】:Copy XML values when matching tags are found找到匹配的标签时复制 XML 值
【发布时间】:2013-04-05 12:47:43
【问题描述】:

第一次在这里发帖,找不到我需要的东西。 我有 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 脚本将比第三方工具更可取。 谢谢!

【问题讨论】:

    标签: xml xslt copy


    【解决方案1】:

    XSLT 3.0 样式表为没有任何子元素的元素生成动态 XPath 字符串,并使用 &lt;xsl:evaluate&gt; 从“base”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>
    

    【讨论】:

      【解决方案2】:

      这是一个 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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-26
        • 2012-08-16
        • 2021-05-03
        • 1970-01-01
        • 1970-01-01
        • 2019-02-19
        • 1970-01-01
        相关资源
        最近更新 更多