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



在过去的两天里,我一直在为此苦苦挣扎。我对 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', '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 个匹配项。我提供的示例输出是使用关键字"日志记录"来匹配服务。

我知道我没有模板或 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(., $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>

你的方法很接近。您成功地屏蔽了不需要的<service>元素,但未能屏蔽出不需要的<server>元素。

简单介绍一下:标识模板将所有节点(元素、属性等)从输入流复制到输出流 - 除非 XSLT 中存在更具体的模板规则。

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

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

以下 XSLT 会屏蔽<service>子项中不包含"日志记录"文本的所有<server>元素,以及不包含"日志记录"文本的所有<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>

PS:我知道这个建议不会被所有人喜欢,但我推荐W3Schools作为XML/XSLT/XPath的介绍性网站。

相关内容

  • 没有找到相关文章

最新更新