首页 > 解决方案 > XSLT 1.0 - 如何排除不包含特定子节点的节点树

问题描述

在过去的两天里,我一直在为此苦苦挣扎。我对 XPATH 和 XSLT 的熟悉程度远不如我所需要的那样,但是时间并没有利于真正深入研究。这是在 BMC 的 TrueSight 编排应用程序中使用的,因此仅限于使用 XPATH/XSLT 1.0。

一些我不介意被重定向到资源以获得解释的附加问题:

  1. 我不熟悉“身份模板”,我已经在几篇文章中看到过它。
  2. 关于如何更有选择性地处理元素、节点和节点集,我不熟悉模板。
    • 我是一名经验丰富的 shell 脚本程序员,因此自上而下的方法对我来说有点陌生,因为它与 XML 文档的处理方式有关。
  3. 当涉及到 XPATH 时,我仍在学习很多术语、轴、关系路径与绝对路径等。我不害怕“谷歌搜索”来寻找答案,但有时拥有一个首选资源比试图猜测哪些关键字可能会提供可能导致答案的结果集更好。
    • 有更推荐的首选资源吗?(我知道这是一个有偏见的问题,但我希望会有一些资源比其他资源更值得推荐。)

我有以下输入文件:

<servers>
  <server os="WINDOWS" role="CENTRAL" account="*****" name="SERVP0001">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
      <service name="QlikSenseEngineService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Engine Service</service>
      <service name="QlikSensePrintingService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Printing Service</service>
      <service name="QlikSenseProxyService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Proxy Service</service>
      <service name="QlikSenseRepositoryDatabase" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Repository Database</service>
      <service name="QlikSenseRepositoryService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Repository Service</service>
      <service name="QlikSenseSchedulerService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Scheduler Service</service>
      <service name="QlikSenseServiceDispatcher" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Service Dispatcher</service>
    </services>
  </server>
  <server os="WINDOWS" role="SLAVE" account="*****" name="SERVP0002">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
      <service name="QlikSenseEngineService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Engine Service</service>
      <service name="QlikSensePrintingService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Printing Service</service>
      <service name="QlikSenseProxyService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Proxy Service</service>
      <service name="QlikSenseRepositoryService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Repository Service</service>
      <service name="QlikSenseSchedulerService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Scheduler Service</service>
      <service name="QlikSenseServiceDispatcher" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Service Dispatcher</service>
    </services>
  </server>
  <server os="WINDOWS" role="NPRINTING" account="*****" name="SERVN0001">
    <services>
      <service name="QlikNPrintingEngine" start_type="AUTOMATIC" state="RUNNING">Qlik NPrinting Engine</service>
      <service name="QlikNPrintingLicenseService" start_type="AUTOMATIC" state="RUNNING">Qlik NPrinting License Service</service>
      <service name="QlikNPrintingMessagingService" start_type="AUTOMATIC" state="RUNNING">QlikNPrintingMessagingService</service>
      <service name="QlikNPrintingRepoService" start_type="AUTOMATIC" state="RUNNING">QlikNPrintingRepoService</service>
      <service name="QlikNPrintingScheduler" start_type="AUTOMATIC" state="RUNNING">Qlik NPrinting Scheduler</service>
      <service name="QlikNPrintingWebEngine" start_type="AUTOMATIC" state="RUNNING">Qlik NPrinting Web Engine</service>
    </services>
  </server>
</servers>

我正在尝试从根元素向下选择,仅包括那些与 @name 属性或文本值匹配的服务节点。

这是我通过几个小时的搜索和反复试验拼凑而成的 XSL。我无法弄清楚的唯一部分是在找不到匹配服务时如何防止打印服务器节点及其后代。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="no" />
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="service">
    <xsl:choose>
      <xsl:when test="contains( translate( normalize-space( ./@name ), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ), translate( normalize-space( &quot;${SERVICENAME}&quot; ), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ) )">
        <xsl:copy-of select="." />
      </xsl:when>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

这是我使用 XSL 得到的当前输出:

<servers>
  <server os="WINDOWS" role="CENTRAL" account="*****" name="SERVP0001">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
    </services>
  </server>
  <server os="WINDOWS" role="SLAVE" account="*****" name="SERVP0002">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
    </services>
  </server>
  <server os="WINDOWS" role="NPRINTING" account="*****" name="SERVN0001">
    <services />
  </server>
</servers>

这是我想要的输出:

<servers>
  <server os="WINDOWS" role="CENTRAL" account="*****" name="SERVP0001">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
    </services>
  </server>
  <server os="WINDOWS" role="SLAVE" account="*****" name="SERVP0002">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
    </services>
  </server>
</servers>

在某些情况下,一个服务器可能有多个匹配服务。例如,如果我使用“engine”作为关键字来匹配服务,我将在 SERVP0001 中匹配 1 个,在 SERVP0002 中匹配 1 个,在 SERVN0001 中匹配 2 个。我提供的示例输出使用关键字“logging”来匹配服务。

我知道我没有用于搜索并返回其文本值包含我的搜索字符串的服务的模板或 Choose-When 元素。 我想如果我可以让属性搜索返回格式正确的文档,我可以修改以适应服务文本。

提前感谢您提供的任何帮助和资源。

标签: xmlxsltxpathxslt-1.0xpath-1.0

解决方案


一个更简单/更短的解决方案(无需将某些内容设置为 1 以记住有命中):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 
 <xsl:param name="pKeyword" select="'logging'"/>
 <xsl:variable name="vKeywordUpper" select="translate($pKeyword, $vLower, $vUpper)"/>
 
 <xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
 <xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="server | service">
    <xsl:if test="descendant-or-self::service
                   /@name[contains(translate(., $vLower, $vUpper), $vKeywordUpper)]">
      <xsl:element name="{name()}">
        <xsl:apply-templates select="@*|node()"/>
      </xsl:element>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时(重新格式化以避免需要水平滚动):

<servers>
  <server os="WINDOWS" role="CENTRAL" account="*****" name="SERVP0001">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Logging Service</service>
      <service name="QlikSenseEngineService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Engine Service</service>
      <service name="QlikSensePrintingService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Printing Service</service>
      <service name="QlikSenseProxyService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Proxy Service</service>
      <service name="QlikSenseRepositoryDatabase" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Repository Database</service>
      <service name="QlikSenseRepositoryService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Repository Service</service>
      <service name="QlikSenseSchedulerService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Scheduler Service</service>
      <service name="QlikSenseServiceDispatcher" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Service Dispatcher</service>
    </services>
  </server>
  <server os="WINDOWS" role="SLAVE" account="*****" name="SERVP0002">
    <services>
      <service name="QlikLoggingService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Logging Service</service>
      <service name="QlikSenseEngineService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Engine Service</service>
      <service name="QlikSensePrintingService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Printing Service</service>
      <service name="QlikSenseProxyService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Proxy Service</service>
      <service name="QlikSenseRepositoryService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Repository Service</service>
      <service name="QlikSenseSchedulerService" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Scheduler Service</service>
      <service name="QlikSenseServiceDispatcher" start_type="AUTOMATIC"
      state="RUNNING">Qlik Sense Service Dispatcher</service>
    </services>
  </server>
  <server os="WINDOWS" role="NPRINTING" account="*****" name="SERVN0001">
    <services>
      <service name="QlikNPrintingEngine" start_type="AUTOMATIC"
      state="RUNNING">Qlik NPrinting Engine</service>
      <service name="QlikNPrintingLicenseService" start_type="AUTOMATIC"
      state="RUNNING">Qlik NPrinting License Service</service>
      <service name="QlikNPrintingMessagingService" start_type="AUTOMATIC"
      state="RUNNING">QlikNPrintingMessagingService</service>
      <service name="QlikNPrintingRepoService" start_type="AUTOMATIC"
      state="RUNNING">QlikNPrintingRepoService</service>
      <service name="QlikNPrintingScheduler" start_type="AUTOMATIC"
      state="RUNNING">Qlik NPrinting Scheduler</service>
      <service name="QlikNPrintingWebEngine" start_type="AUTOMATIC"
      state="RUNNING">Qlik NPrinting Web Engine</service>
    </services>
  </server>
</servers>

产生了想要的正确结果

<servers>
   <server os="WINDOWS" role="CENTRAL" account="*****" name="SERVP0001">
      <services>
         <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
      </services>
   </server>
   <server os="WINDOWS" role="SLAVE" account="*****" name="SERVP0002">
      <services>
         <service name="QlikLoggingService" start_type="AUTOMATIC" state="RUNNING">Qlik Logging Service</service>
      </services>
   </server>
</servers>

如果我们更改<xsl:param name="pKeyword" select="'logging'"/><xsl:param name="pKeyword" select="'engine'"/>运行转换,则会再次产生预期的正确结果:

<servers>
   <server os="WINDOWS" role="CENTRAL" account="*****" name="SERVP0001">
      <services>
         <service name="QlikSenseEngineService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Engine Service</service>
      </services>
   </server>
   <server os="WINDOWS" role="SLAVE" account="*****" name="SERVP0002">
      <services>
         <service name="QlikSenseEngineService" start_type="AUTOMATIC" state="RUNNING">Qlik Sense Engine Service</service>
      </services>
   </server>
   <server os="WINDOWS" role="NPRINTING" account="*****" name="SERVN0001">
      <services>
         <service name="QlikNPrintingEngine" start_type="AUTOMATIC" state="RUNNING">Qlik NPrinting Engine</service>
         <service name="QlikNPrintingWebEngine" start_type="AUTOMATIC" state="RUNNING">Qlik NPrinting Web Engine</service>
      </services>
   </server>
</servers>

推荐阅读