如何简化相同的代码块?

How to simplify identical blocks of code?

输入xml文件:

<Config>
  <Fix title="title" description="description" React="200" Lock="E0" Hold="50">
    <Name type="type" value="value" Hold="50">
      <Image select="2" readonly="false" />
    </Name>
  </Fix>
</Config>

从中获取输入文件更新的“updates.xml”文件 - 带有 const="?" 的 Lock、React 和 Hold 标签属性:

<Macro>
  <Item key="Element">
    <tag1 title="title1" description="description1" />
    <tag2 title="title2" description="description2" />
    <tag3 title="title3" description="description3" />
  </Item>
  <Item key="Attribute">
    <Lock title="title4" const="?" description="description4" />
    <React title="title5" const="?" description="description5" />
    <Hold title="title6" const="?" description="description6" />
  </Item>
</Macro>

如果输入文件包含“updates.xml”文件中的标签,则将“updates.xml”文件中的属性复制到其中。此外,如果输入文件中的标签具有 Lock、React 或 Hold 属性,则在转换后,这些属性成为子标签,而不是属性,并且输入文件中的 Lock、React 或 Hold 属性的值成为分别为转换后文件的 Lock、React 或 Hold 标签的 const 属性值。 XSLT1.0 转换后的文件应该是这样的:

<Config>
  <Fix title="title" description="description">
    <Lock const="E0" title="title4" description="description4" />
    <React const="200" title="title5" description="description5" />
    <Hold const="50" title="title6" description="description6" />
    <Name type="type" value="value">
      <Hold const="50" title="title6" description="description6" />
      <Image select="2" readonly="false" />
    </Name>
  </Fix>
</Config>

XSLT1.0 转换文件具有三个相同的 Lock、React 或 Hold 标签选择代码块:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  exclude-result-prefixes="exslt"
  version="1.0">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:strip-space  elements="*"/>

  <xsl:output method="xml" indent="yes" />
  <xsl:variable name="updates" select="document('updates.xml')"/>

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

  <xsl:template match="*">
    <xsl:copy>

      <xsl:variable name="name" select="name()"/>
      <xsl:apply-templates select="$updates//*[name(.) = $name]/@*"/>
      <xsl:apply-templates select="@*"/>

      <xsl:choose>
        <xsl:when test="self::node()[@Lock]">
          <Lock const="{@Lock}">
            <xsl:copy-of select="$updates//Lock/@*[name(.) != 'const']"/>
          </Lock>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="($updates//*[name(.) = $name])[@Lock]">
            <Lock>
              <xsl:attribute name="const">
                <xsl:value-of select="$updates//*[name(.) = $name]/@Lock"/>
              </xsl:attribute>
              <xsl:copy-of select="$updates//Lock/@*[name(.) != 'const']"/>
            </Lock>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:choose>
        <xsl:when test="self::node()[@React]">
          <React const="{@React}">
            <xsl:copy-of select="$updates//React/@*[name(.) != 'const']"/>
          </React>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="($updates//*[name(.) = $name])[@React]">
            <React>
              <xsl:attribute name="const">
                <xsl:value-of select="$updates//*[name(.) = $name]/@React"/>
              </xsl:attribute>
              <xsl:copy-of select="$updates//React/@*[name(.) != 'const']"/>
            </React>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:choose>
        <xsl:when test="self::node()[@Hold]">
          <Hold const="{@Hold}">
            <xsl:copy-of select="$updates//Hold/@*[name(.) != 'const']"/>
          </Hold>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="($updates//*[name(.) = $name])[@Hold]">
            <Hold>
              <xsl:attribute name="const">
                <xsl:value-of select="$updates//*[name(.) = $name]/@Hold"/>
              </xsl:attribute>
              <xsl:copy-of select="$updates//Hold/@*[name(.) != 'const']"/>
            </Hold>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@Lock" />
  <xsl:template match="@React" />
  <xsl:template match="@Hold" />

</xsl:stylesheet>

如何将 Lock、React 或 Hold 标签的三个选择块转换为一个?

您可以参数化模板并调用它或apply-templates(使用模式):

  <xsl:template match="*">
    <xsl:copy>

      <xsl:variable name="name" select="name()"/>
      <xsl:apply-templates select="$updates//*[name(.) = $name]/@*"/>
      <xsl:apply-templates select="@*"/>

      <xsl:apply-templates select="." mode="compare">
        <xsl:with-param name="att" select="'Lock'"/>
      </xsl:apply-templates>
      
      <xsl:apply-templates select="." mode="compare">
        <xsl:with-param name="att" select="'React'"/>
      </xsl:apply-templates>
      
      <xsl:apply-templates select="." mode="compare">
        <xsl:with-param name="att" select="'Hold'"/>
      </xsl:apply-templates>

      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="*" mode="compare">
    <xsl:param name="att"/>
    <xsl:variable name="name" select="name()"/>
    <xsl:choose>
      <xsl:when test="self::node()[@*[name() = $att]]">
      <xsl:element name="{$att}"> 
        <xsl:attribute name="const">
          <xsl:value-of select="@*[name() = $att]"/>
        </xsl:attribute>
        <xsl:copy-of select="$updates//*[name() = $att]/@*[name(.) != 'const']"/>
      </xsl:element>
      </xsl:when>
      <xsl:otherwise>
      <xsl:if test="($updates//*[name(.) = $name])[@*[name() = $att]]">
        <Lock>
          <xsl:attribute name="const">
            <xsl:value-of select="$updates//*[name(.) = $name]/@*[name() = $att]"/>
          </xsl:attribute>
          <xsl:copy-of select="$updates//*[name() = $att]/@*[name(.) != 'const']"/>
        </Lock>
      </xsl:if>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

这样代码不一定会变得更易读。