XSLT + XML - &gt; Word <field name =“for”> Value </field>

时间:2012-11-05 20:33:02

标签: mysql xml xslt

我有一个带有MySQL Query Browser的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 select

<?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模板中更容易使用(对我而言)。

3 个答案:

答案 0 :(得分: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匹配源的一小部分并递归应用其他模板。此模式中最重要和最常见的模板是身份模板,它复制输出。你应该阅读This is a good tutorial on XSLT coding patterns

更新

下面是一个完整的解决方案,它将生成 text 输出(因为这是你想要的,而不是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

答案 1 :(得分:1)

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

由于您的XML包含多个字段,因此您仍需要稍微扩展XSLT文件以迭代字段,或者(正如Francis Avila建议的那样)编写模板。但是这两种方法都没问题。这两种方法的技术术语分别是“拉”和“推”。

当然,由于您最终想要生成单词表,因此您必须生成w:trw:tc元素等。

答案 2 :(得分: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&#39;以外的<field> <row>个孩子。或者&#39; VulnName&#39;将从您的报告中删除。更通用的解决方案可能如下所示:

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

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