如何为两个 xml 文件添加合并模板?

How to add a merge template for two xml files?

第一个 xml 文件 in.xml:

<conf title="co" attr01="val01">
  <c attr02="val02">
    <Item key="db" attr03="val03">
    <m1 />
      <s>
        <m2 />
        <Item key="1">
        <m3 />
          <sp>
            <Chart />
          </sp>
        </Item>
        <m4 />
        <Item key="2">
          <sp>
            <m5 />
            <Chart />
          </sp>
        </Item>
        <Item key="3">
          <sp>
            <Chart />
          </sp>
        </Item>
        <m6 />
      </s>
    </Item>
  </c>
</conf>

第二个 xml 文件 profile.xml,它完成了第一个包含小部件信息的文件:

<p title="profile">
  <c>
    <Item key="db">
      <s>
        <Item key="1">
          <sp>
            <Chart>
                <Widget title="widget12">
                    <Test title="test1"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <Item key="2">
          <sp>
            <Chart>
                <Widget title="widget32">
                    <Test title="test3"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <Item key="3">
          <sp>
            <Chart>
                <Widget title="widget54">
                    <Test title="test6"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
      </s>
    </Item>
  </c>
</p>

profile.xml文件与in.xml的区别在于根标签名称不同,每个Chart标签都有对应的子Widget标签,只有相对于Chart标签的父标签存在于文件和除root和Widget之外的所有标签都只有键属性。

转换后的文件与原始文件仅在 Chart 标签的 Widget 子标签不同:

<conf title="co" attr01="val01">
  <c attr02="val02">
    <Item key="db" attr03="val03">
    <m1 />
      <s>
        <m2 />
        <Item key="1">
        <m3 />
          <sp>
            <Chart>
                <Widget title="widget12">
                    <Test title="test1"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <m4 />
        <Item key="2">
          <sp>
            <m5 />
            <Chart>
                <Widget title="widget32">
                    <Test title="test3"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <Item key="3">
          <sp>
            <Chart>
                <Widget title="widget54">
                    <Test title="test6"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <m6 />
      </s>
    </Item>
  </c>
</conf>

现在有一个不完整的 XSLT1.0 转换文件:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:param name="updates" select="document($fileName)" />

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

如何重写“p”模板XSLT1.0?

试试这个:

convert.xsl

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:param name="updates" select="document($fileName)" />
  
  <!--transform starts here on input XML-->
  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>
  
  <!--rules for general nodes()-->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!--special rule for Chart elements-->      
  <xsl:template match="Chart">
    <xsl:variable name="context" select="."/>
    <xsl:if test="$updates//Item/@key = $context/ancestor-or-self::Item[1]/@key">
      <xsl:copy-of select="($updates//Item[@key = $context/ancestor-or-self::Item[1]/@key])//Chart"/>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

output.xml

<?xml version="1.0" encoding="UTF-8"?>
<conf title="co" attr01="val01">
   <c attr02="val02">
      <Item key="db" attr03="val03">
         <m1/>
         <s>
            <m2/>
            <Item key="1">
               <m3/>
               <sp>
                  <Chart>
                     <Widget title="widget12">
                        <Test title="test1"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m4/>
            <Item key="2">
               <sp>
                  <m5/>
                  <Chart>
                     <Widget title="widget32">
                        <Test title="test3"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <Item key="3">
               <sp>
                  <Chart>
                     <Widget title="widget54">
                        <Test title="test6"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m6/>
         </s>
      </Item>
   </c>
</conf>

如果需要,您可以将参数 profile.xml 更改为其他参数...

不使用密钥

如果您的属性 @attr01-@attr03 很重要?:

<?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" version="1.0" encoding="UTF-8" indent="yes"/>
  
  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:variable name="updates" select="document($fileName)/*" />
  
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="Chart">
    <xsl:variable name="idattr01" select="ancestor::*[@attr01]/@attr01"/>
    <xsl:variable name="idattr02" select="ancestor::*[@attr02]/@attr02"/>
    <xsl:variable name="idattr03" select="ancestor::*[@attr03]/@attr03"/>
    <xsl:variable name="key"      select="ancestor::*[@key][1]/@key"/>
    <xsl:variable name="chartFromUpdate" select="$updates[@attr01=$idattr01]/c[@attr02=$idattr02]/Item[@attr03=$idattr03]/s/Item[@key=$key]/sp/Chart" />
    <xsl:choose>
      <xsl:when test="$chartFromUpdate">
        <xsl:copy-of select="$chartFromUpdate"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

或者您也可以这样使用 key-function:

<?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" version="1.0" encoding="UTF-8" indent="yes"/>
  
  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:variable name="updates" select="document($fileName)" />
  
  <xsl:key name="item" match="Item" use="@key"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="Chart">
    <xsl:variable name="key"      select="ancestor::*[@key][1]/@key"/>
    <xsl:variable name="chartFromUpdate">
      <!-- Since the lookup is in an different document we have to change the context -->
      <xsl:for-each select="$updates">
        <xsl:copy-of select="key('item', $key)/sp/Chart"/>
      </xsl:for-each>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="$chartFromUpdate">
        <xsl:copy-of select="$chartFromUpdate"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>    
  </xsl:template>
  
</xsl:stylesheet>

关于在使用 xslt 1.0 的不同文档上使用 key-function,请参阅 this answer

对于两个 xslt,这是输出:

<?xml version="1.0" encoding="UTF-8"?>
<conf title="co" attr01="val01">
   <c attr02="val02">
      <Item key="db" attr03="val03">
         <m1/>
         <s>
            <m2/>
            <Item key="1">
               <m3/>
               <sp>
                  <Chart>
                     <Widget title="widget12">
                        <Test title="test1"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m4/>
            <Item key="2">
               <sp>
                  <m5/>
                  <Chart>
                     <Widget title="widget32">
                        <Test title="test3"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <Item key="3">
               <sp>
                  <Chart>
                     <Widget title="widget54">
                        <Test title="test6"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m6/>
         </s>
      </Item>
   </c>
</conf>