You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSLT技术实现:如何跳过缺失指定子元素的节点?

问题描述

我编写的程序可将周期性测量数据转换为XML格式,但有时会出现测量失败的情况(如连接失败)。为保证输出一致性,程序仍会输出内容,此时原始值标记为U(代表undefined,未定义),转换器生成XML时会省略value元素。

sample元素的内容模型如下(省略大部分细节):

<!ELEMENT sample (label, value?, unit?, thresholds?, range?)>
<!ATTLIST sample
          label CDATA #REQUIRED>

可见value是可选元素,以下是不含value的示例:

<sample label="in">
      <label>in</label>
      <range min="0"/>
    </sample>

我需要将该XML转换为特定输出格式,希望跳过内部不含value节点的sample节点,但不知如何实现。

sample元素所在的内容模型:

<!ELEMENT perf_data (sample+, warn_labels?, crit_labels?)>
<!ATTLIST perf_data
          count CDATA #REQUIRED>

目前XSL使用的选择器为:

<xsl:template match="perf_data/sample[@label]">

实际示例上下文(两个无value的sample节点):

<perf_data count="2">
    <sample label="in">
      <label>in</label>
      <range min="0"/>
    </sample>
    <sample label="out">
      <label>out</label>
      <range min="0"/>
    </sample>
  </perf_data>

直接使用match="perf_data/sample[@label]/value"会导致转换失败。


补充示例

输入XML(std_dev节点不含value)

<?xml version="1.0" encoding="utf-8"?>
<MonitoringOutput id="id-18895" version="0.2">
  <perf_data count="3">
    <sample label="last">
      <label>last</label>
      <value>0.508798</value>
      <range min="0"/>
    </sample>
    <sample label="std_dev">
      <label>std_dev</label>
      <range min="0"/>
    </sample>
    <sample label="avg">
      <label>avg</label>
      <value>0.508798</value>
      <range min="0"/>
    </sample>
  </perf_data>
</MonitoringOutput>

现有XSLT代码

<xsl:template match="/MonitoringOutput[@version]">
  <xsl:comment><xsl:value-of select="name(.)"
  /> v<xsl:value-of select="@version" /> id=<xsl:value-of select="@id"
  /> (PRTG.xsl v0.0.2)</xsl:comment>
  <xsl:variable name="main.node" select="." />
  <prtg>
    <xsl:choose>
      <xsl:when test="perf_data[@count &gt; 0]">
        <xsl:apply-templates select="$main.node/perf_data"/>
      </xsl:when>
      <xsl:otherwise>
        <Error>1</Error>
      </xsl:otherwise>
    </xsl:choose>
  </prtg>
</xsl:template>

<xsl:template match="perf_data[@count &gt; 0]">
  <!-- no parsing error -->
  <xsl:variable name="main.node" select=".." />
  <xsl:variable name="perf.node" select="." />
  <xsl:apply-templates select="$perf.node/sample"/>
</xsl:template>

<xsl:template match="perf_data/sample[@label]">
  <result>
    <xsl:apply-templates />
  </result>
</xsl:template>

<xsl:template match="sample/label">
  <Channel><xsl:value-of select="."/></Channel>
</xsl:template>

<xsl:template match="sample/range[@*]">
  <xsl:for-each select="@*">
    <xsl:comment>range: <xsl:value-of select="name(.)" />="<xsl:value-of
    select="."/>"</xsl:comment>
  </xsl:for-each>
</xsl:template>

<xsl:template match="sample/value">
  <Value><xsl:value-of select="."/></Value>
</xsl:template>

解决方案

有两种简单的方式可以实现跳过不含value的sample节点:

方法一:修改节点选择条件

在perf_data模板中,修改apply-templates的选择器,只选中包含value子元素的sample节点:

<xsl:template match="perf_data[@count &gt; 0]">
  <!-- no parsing error -->
  <xsl:variable name="main.node" select=".." />
  <xsl:variable name="perf.node" select="." />
  <!-- 仅处理包含value子元素的sample -->
  <xsl:apply-templates select="$perf.node/sample[value]"/>
</xsl:template>

这种方式从源头上过滤掉不需要处理的节点,只对符合条件的sample执行后续模板。

方法二:添加空模板跳过节点

添加一个匹配**不含value的sample**的空模板,优先级高于原有模板,这样这类节点不会输出任何内容:

<!-- 空模板:跳过所有不含value的sample节点 -->
<xsl:template match="perf_data/sample[@label][not(value)]" />

<!-- 原有模板:仅处理含value的sample节点 -->
<xsl:template match="perf_data/sample[@label]">
  <result>
    <xsl:apply-templates />
  </result>
</xsl:template>

因为XSLT会优先匹配更具体的模板,所以不含value的sample会被空模板匹配,不产生输出;而含value的sample会走原有模板生成<result>节点。


内容的提问来源于stack exchange,提问作者U. Windl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 06:09:49