2

我有一个带有 MySQL 查询浏览器的 XML 产品。我正在尝试应用 XSLT 将结果输出到 Word 表中。每条记录一张表。

这是我的 XML 示例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ROOT SYSTEM "Nessus.dtd">
<ROOT>
    <row>
      <field name="Niveau">Critique</field>
      <field name="Name">Apache 2.2 &lt; 2.2.15 Multiple Vulnerabilities</field>
    </row>
    <row>
      <field name="Niveau">Critique</field>
      <field name="VulnName">Microsoft Windows 2000 Unsupported Installation Detection</field>
    </row>
    <row>
      <field name="Niveau">Haute</field>
      <field name="VulnName">CGI Generic SQL Injection</field>
    </row>
</ROOT>

对于 XLST,我已经发现我需要做一个 for-each 选择

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

    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
<xsl:for-each select="ROOT/row">
  Niveau : <xsl:value-of select="????"/>
  Name   : <xsl:value-of select="????"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

当我执行此循环时,我会看到与文件中相同数量的空表<row></row>

但是我还没有找到正确的“选择价值=”的方法。我没有运气就尝试了以下方法。

<xsl:value-of select="@name"/>
<xsl:value-of select="name"/>
<xsl:value-of select="@row/name"/>
<xsl:value-of select="row/@name"/>
<xsl:value-of select="@ROOT/row/name"/>

还有一些我不记得了。知道我需要什么来制作请求以获取结果文件中的值吗?

我刚刚尝试过:

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

    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
<xsl:for-each select="ROOT/row">
  Niveau : <xsl:value-of select="field/@Niveau"/>
  Name   : <xsl:value-of select="field/@name"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

它输出这个:

NIVEAU :  
NAME   : name

NIVEAU :  
NAME   : Niveau

NIVEAU :  
NAME   : Niveau

我想要这个输出:

NIVEAU : Critique
NAME   : Apache 2.2 &lt; 2.2.15 Multiple Vulnerabilities

NIVEAU : Critique
NAME   : Microsoft Windows 2000 Unsupported Installation Detection

NIVEAU : Haute
NAME   : CGI Generic SQL Injection

任何帮助,将不胜感激。

谢谢你。

更新

现在有了这个 XSLT

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:output method="text"/>

  <xsl:strip-space elements="*"/>

  <xsl:template match="row">
    <xsl:text>NIVEAU : </xsl:text>
    <xsl:value-of select="field[@name = 'Niveau']"/>
    <xsl:text>&#xa;</xsl:text>
    <xsl:text>NAME   : </xsl:text>
    <xsl:value-of select="field[@name = 'Name']"/>
    <xsl:text>&#xa;&#xa;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

我得到这个输出:

<?xml version="1.0"?>
NIVEAU : 
NAME   : Apache 2.2 &lt; 2.2.15 Multiple Vulnerabilities

NIVEAU : Critique
NAME   : Microsoft Windows 2000 Unsupported Installation Detection

NIVEAU : Haute
NAME   : CGI Generic SQL Injection

如您所见,第一个字段为空。老实说,我可以忍受并手动填写它,但如果你明白为什么会发生这种情况,我会非常高兴:)

更新

使用<xsl:value-of select="field[@name = 'foo']"/>给了我想要的价值。我保留了 for-each,因为它(对我来说)在 MS Word 模板中更容易使用。

4

3 回答 3

3

for-each通常是 XSLT 中的代码异味。您很可能需要一个模板,而不是一个 for-each 循环:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
    <root>
        <xsl:apply-templates/>
    </root>
</xsl:template>

<xsl:template match="field">
<xsl:value-of select="@name"/> : <xsl:value-of select="."/>
</xsl:template>

</xsl:stylesheet>

该模板将产生以下输出:

<root>
    
      Niveau : Critique
      name : Apache 2.2 &lt; 2.2.15 Multiple Vulnerabilities
    
    
      Niveau : Critique
      name : Microsoft Windows 2000 Unsupported Installation Detection
    
    
      Niveau : Haute
      name : CGI Generic SQL Injection
    
</root>

XSLT 是为这种使用模式而设计的——许多xsl:template匹配源的一小部分并递归地应用其他模板。此模式中最重要和最常见的模板是身份模板,它复制输出。这是一个很好的关于 XSLT 编码模式的教程,你应该阅读。

更新

下面是一个完整的解决方案,它将产生文本输出(因为这似乎是您想要的,而不是 XML 输出)。我不确定 xsl 是不是最好的语言,但它可以工作....

请注意,我们唯一使用的地方for-each是用于排序以确定最长名称的长度。我们使用模板和模式匹配select来使所有其他循环隐含。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="newline"><xsl:text>
</xsl:text></xsl:variable>

  <!-- determine the maximum length of the "name" field so we can pad with spaces for all shorter items -->
  <xsl:variable name="max_name_len">
    <xsl:for-each select="ROOT/row/field/@name">
      <xsl:sort select="string-length(.)" data-type="number" order="descending"/>
      <xsl:if test="position() = 1">
        <xsl:value-of select="string-length(.)"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <!-- for each row, apply templates then add a blank line -->
  <xsl:template match="row">
    <xsl:apply-templates/>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <!-- for each field, apply template to name and add value, followed by a newline -->
  <xsl:template match="field">
    <xsl:apply-templates select="@name"/> : <xsl:value-of select="concat(., $newline)"/>
  </xsl:template>


  <!-- for each name, uppercase and pad with spaces to the right -->
  <xsl:template match="field/@name">
    <xsl:call-template name="padright">
      <xsl:with-param name="text">
        <xsl:call-template name="toupper">
          <xsl:with-param name="text" select="."/>
        </xsl:call-template>
      </xsl:with-param>
      <xsl:with-param name="len" select="$max_name_len"/>
    </xsl:call-template>
  </xsl:template>


  <!-- Utility function: uppercase a string -->
  <xsl:template name="toupper">
    <xsl:param name="text"/>
    <xsl:value-of select="translate($text, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
  </xsl:template>

  <!-- Utility function: pad a string to desired len with spaces on the right -->
  <!-- uses a recursive solution -->
  <xsl:template name="padright">
    <xsl:param name="text"/>
    <xsl:param name="len"/>
    <xsl:choose>
      <xsl:when test="string-length($text) &lt; $len">
        <xsl:call-template name="padright">
          <xsl:with-param name="text" select="concat($text, ' ')"/>
          <xsl:with-param name="len" select="$len"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

此样式表产生以下输出:

NIVEAU : Critique
NAME   : Apache 2.2 < 2.2.15 Multiple Vulnerabilities

NIVEAU : Critique
NAME   : Microsoft Windows 2000 Unsupported Installation Detection

NIVEAU : Haute
NAME   : CGI Generic SQL Injection
于 2012-11-05T20:45:21.980 回答
1

当你这样做<xsl:for-each select="ROOT/row">时,循环内的当前上下文就是row元素。因此,为了访问字段名称,您需要编写,例如<xsl:value-of select="field/@name"/>.

由于您的 XML 包含多个字段,您仍然需要扩展您的 XSLT 文件以迭代这些字段,或者(如 Francis Avila 建议的那样)编写一个模板。不过,这两种方法都可以。这两种方法的技术术语分别是“拉”和“推”。

当然,既然你最终想要生成一个词表,你将不得不生成w:trw:tc元素等。

于 2012-11-05T20:56:27.207 回答
0

此样式表将为您提供您想要的输出

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:output method="text"/>

  <xsl:strip-space elements="*"/>

  <xsl:template match="row">
    <xsl:text>NIVEAU : </xsl:text>
    <xsl:value-of select="field[@name eq 'Niveau']"/>
    <xsl:text>&#xa;</xsl:text>
    <xsl:text>NAME   : </xsl:text>
    <xsl:value-of select="field[@name eq 'VulnName']"/>
    <xsl:text>&#xa;&#xa;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

这对您的输入 xml 和输出文本非常具体,例如,“Niveau”或“ <field>VulnName <row>”以外的任何子项都将从您的报告中删除。更通用的解决方案可能如下所示:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:output method="text"/>

  <xsl:template match="field">
    <xsl:value-of select="concat(upper-case(@name),': ',.)"/>
  </xsl:template>

</xsl:stylesheet>

此解决方案虽然与您所需输出中的空白格式不完全匹配,但它确实捕获了所有可能的字段。

于 2012-11-05T23:15:45.773 回答