如何将信息从内部元素传播到外部元素?

How to propagate information from inner elements to outer element?

提问人:U. Windl 提问时间:10/4/2023 最后编辑:U. Windl 更新时间:10/6/2023 访问量:40

问:

对于XSLT:当缺少特定的内部子元素时如何跳过元素?中描述的内容模型,我有一个新问题:

我需要在外部级别创建不同的输出,具体取决于匹配内部级别。 例如,请考虑以下部分:

<!-- ... -->
  <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>
<!-- ... -->

有两个元素,但其中没有一个元素包含元素,这实际上使它们变得无用。samplevalue

我写了一条规则来“冗长地忽略”这些元素,如下所示:

<xsl:template match="perf_data/sample[@label and not(value)]">
  <xsl:comment>Skipped incomplete sample "<xsl:value-of select="label"
  />"</xsl:comment>
</xsl:template>

但是,我还需要的是,如果忽略所有示例元素,那么我必须在输出(也许)上创建一个额外的外部元素,否则在“级别”上。<Error>1</Error><Error>0</Error>perf_data

但是我不知道如何实现它,特别是因为您不能在变量中具有计数器;否则我会暗示计算有效的 s 并检查计数。 我该怎么做?sample

另请参阅 XSLT:选择一个节点,其中其子节点的非子节点具有给定的属性(即 ¬∃ (∄))。

示例输入

下面是两个输入示例,一个好的,一个坏的:

不良样品

<?xml version="1.0" encoding="utf-8"?>
<MonitoringOutput id="id-3150" version="0.2">
  <description>snmp_netif-1.out</description>
  <exit_code>0</exit_code>
  <status_string>Previous state not found, U octets/s in [delta 0s], U octets/s out [delta 0s]</status_string>
  <perf_string> in=U;;;0 out=U;;;0</perf_string>
  <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>
</MonitoringOutput>

好样品

<?xml version="1.0" encoding="utf-8"?>
<MonitoringOutput id="id-3160" version="0.2">
  <description>snmp_netif-2.out</description>
  <exit_code>0</exit_code>
  <status_string>bond0</status_string>
  <info_string>1560.23 octets/s in [delta 10s], 350.921 octets/s out [delta 10s]</info_string>
  <perf_string> in=1560.23B;;;0 out=350.921B;;;0</perf_string>
  <perf_data count="2">
    <sample label="in">
      <label>in</label>
      <value unit="B">1560.23</value>
      <unit>B</unit>
      <range min="0"/>
    </sample>
    <sample label="out">
      <label>out</label>
      <value unit="B">350.921</value>
      <unit>B</unit>
      <range min="0"/>
    </sample>
  </perf_data>
</MonitoringOutput>

因此,所有 s 中的缺失应该“向上”传播到处理时,应将其视为空或不存在。<value><sample><perf_data>

XSLT 匹配

评论

0赞 michael.hor257k 10/4/2023
如果在与父项匹配的模板中执行此操作,则可以计算满足该条件的子项数。然后,如果所有(或没有)都做某事,你可以做某事,如果有些人做,你可以做另一件事。或者,您可以将处理结果存储在变量中,然后在将变量传递给输出之前对变量中的节点进行计数。perf_datasample
0赞 U. Windl 10/4/2023
stackoverflow.com/a/9608675/6607497 说您不能使用 XSLT 变量进行计数。
0赞 michael.hor257k 10/4/2023
恐怕你完全没有抓住我的意思。如果您有一个包含已处理节点的变量,则可以使用该函数计算变量中有多少条注释。显然,您正在从过程语言的角度思考,通过在循环中递增变量来计数。这在像 XSLT 这样的函数式语言中很少需要(尽管仍然可能)。count()

答:

1赞 Siebe Jongebloed 10/6/2023 #1
Do you mean something like this 

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="/Document[not(perf_data/sample[not(@label and not(value))])]">
    <Error>1</Error>
  </xsl:template>

  <xsl:template match="perf_data[sample[@label and not(value)]]">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <Error>0</Error>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>

如果我们有这个 xml:

<Document>
  <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>
  <perf_data count="2">
    <sample label="in">
      <label>in</label>
      <range min="0"/>
    </sample>
    <sample label="out">
      <label>out</label>
      <range min="0"/>
      <value/>  <!-- value-element present -->
    </sample>
  </perf_data>
</Document>

此 xslt 将给出此结果

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

如果我们有这个 xml:

<Document>
  <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>
  <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>
</Document>

它将给出以下结果:

<Error>1</Error>

评论

0赞 U. Windl 10/6/2023
我明白了,你的XSL技能比我好得多。实际上,我已经制定了一个稍微复杂一些的解决方案,但为了完整起见,我也将在这里提供它。
0赞 U. Windl 10/6/2023
不幸的是,对于我刚刚添加到问题中的坏例子,您的 XSLT 无法正常工作:我得到输出内部。perf_data<Error>0</Error>
0赞 U. Windl 10/6/2023 #2

我设法处理了这样的情况(感谢 michael-hor257k 给出的提示):

<xsl:template match="/MonitoringOutput[@version]">
  <xsl:variable name="main.node" select="." />
  <Prtg>
  <!-- ... -->
    <xsl:choose>
      <xsl:when test="count($main.node/perf_data/sample[value]) = 0">
        <xsl:comment>(no valid sample)</xsl:comment>
        <Error>1</Error>
        <!-- ... -->
      </xsl:when>
      <xsl:when test="perf_data[@count &gt; 0]">
        <xsl:comment>(non-empty perf_data)</xsl:comment>
        <xsl:apply-templates select="$main.node/perf_data"/>
        <!-- ... -->
      </xsl:when>
      <xsl:otherwise>
        <xsl:comment>(empty perf_data)</xsl:comment>
        <Error>1</Error>
        <!-- ... -->
      </xsl:otherwise>
    </xsl:choose>
  </Prtg>
</xsl:template>