XSLT 1.0-如何排除不包含特定子项的节点树

问题描述

过去2天,我一直在为此苦苦挣扎。我对XPATH和XSLT的了解不如我所需要的那样,但是时间却不利于真正地深入研究。 BMC的TrueSight orchestration应用程序中使用了此功能,因此仅限于使用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' ) )">
        <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元素来搜索并返回其文本值包含我的搜索字符串的服务。 我认为如果可以得到属性搜索以返回格式正确的文档,我可以对其进行修改以适合服务文本。

在此先感谢您提供的任何帮助和资源。

解决方法

更简单/更短的解决方案(无需将某些内容设置为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(.,$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>
,

您的方法接近了。您成功屏蔽了不需要的<service>元素,但没有屏蔽了不需要的<server>元素。

简单介绍一下:身份模板将所有节点(元素,属性,...)从输入流复制到输出流-除非有更具体的模板中存在em>规则。

在XSLT中,您为元素<service>定义了更具体的模板规则-因此,您在这方面取得了成功。但是您没有为<server>元素定义更具体的模板规则,因此所有这些元素都可以由 identity template 复制。

我不知道 BMC的TrueSight Orchestration应用程序,因此我将匹配的字符串定义为xsl:variables(必要时也可以使用xsl:param)。用${SERVICENAME}或程序需要的任何内容替换单引号中的字符串。 @*复制所有属性。

以下XSLT确实屏蔽了<server>子代中所有不包含“ LOGGING”文本的所有<service>元素以及所有不包含“ LOGGING”的所有<service>元素文字:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="no" />
  <xsl:strip-space elements="*" />
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="server|service">
    <xsl:variable name="matching">
      <xsl:for-each select="descendant-or-self::service/@name">
        <xsl:if test="contains(translate(normalize-space(.),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'),'LOGGING')">1</xsl:if>
      </xsl:for-each>
    </xsl:variable>
    <xsl:if test="contains($matching,'1')">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()" />
      </xsl:copy>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

其输出是:

<?xml version="1.0"?>
<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>

P.S .:我知道这个建议不会被每个人都喜欢,但是我建议W3Schools作为XML / XSLT / XPath的入门网站。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...