【问题标题】:XSLT dynamically generate html table for any XML sectionXSLT 为任何 XML 部分动态生成 html 表
【发布时间】:2015-07-24 00:04:30
【问题描述】:

我有一个由一系列脚本动态生成的 XML 文件。每个部分都可以有名称值对和/或信息表。

我正在尝试创建一个输出每个部分的 XSLT 转换,如果它包含 Format='Table' 则动态生成一个表。

我的示例 XML 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<QAReport ReportDate="05/03/2015 14:07:33" ReportVersion="3.0" ScriptVersion="1.2015.0428.01" QAStatus="2">
  <DiskInformation Display="Disk Information">
    <Disks Format="Table">
      <Disk>
        <DriveLetter>C:</DriveLetter>
        <FoundInSBB>True</FoundInSBB>
        <StartingOffset>
        </StartingOffset>
        <BlockSize>4096</BlockSize>
        <DriveSize>50</DriveSize>
      </Disk>
      <Disk>
        <DriveLetter>D:</DriveLetter>
        <FoundInSBB>True</FoundInSBB>
        <StartingOffset>
        </StartingOffset>
        <BlockSize>4096</BlockSize>
        <DriveSize>50</DriveSize>
      </Disk>
    </Disks>
  </DiskInformation>
  <DeviceManagerInformation Display="Device Manager Information">
    <DeviceManagerErrors Format="Table" />
  </DeviceManagerInformation>
  <EventLogInformation Display="Event Log Information">
    <SystemEventLogErrors Format="Table">
      <Event>
        <EventID>10009</EventID>
        <EventTime>04/27/2015 23:14:20</EventTime>
        <EventSource>DCOM</EventSource>
        <EventMessage>The description for Event ID  -1073731815  in Source  DCOM  cannot be found.  The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them.  The following information is part of the event: WCDRE56I </EventMessage>
      </Event>
      <Event>
        <EventID>10009</EventID>
        <EventTime>04/27/2015 23:14:12</EventTime>
        <EventSource>DCOM</EventSource>
        <EventMessage>The description for Event ID  -1073731815  in Source  DCOM  cannot be found.  The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them.  The following information is part of the event: WCDRE56I </EventMessage>
      </Event>

    </SystemEventLogErrors>
  </EventLogInformation>
  <IISInformation Display="IIS Information">
    <IISService Display="IIS Service Status">Running</IISService>
  </IISInformation>
  <IPInformation Display="IP Information" ReportLevel="20">
    <FQDN>Server123.mydomain.com</FQDN>
    <DNSDomain Display="DNS Domain">mydomain.com</DNSDomain>
    <IPAddress Display="IP Address">10.0.0.101</IPAddress>
    <Subnet>255.255.248.0/64</Subnet>
    <Gateway>10.0.0.1</Gateway>
    <DNSServers Display="DNS Servers" Format="List">
      <DNSServer>12.0.0.1</DNSServer>
      <DNSServer>12.0.0.2</DNSServer>
    </DNSServers>
    <DNSSearchSuffixes Display="DNS Search Suffixes" Format="List">
      <DNSSearchSuffix>MyDomain.com.com</DNSSearchSuffix>
      <DNSSearchSuffix>MyOtherDomain.com</DNSSearchSuffix>
      <DNSSearchSuffix>MyThirdDomain.com</DNSSearchSuffix>
    </DNSSearchSuffixes>
    <DNSRegistrationEnabled>True</DNSRegistrationEnabled>
    <NetBIOS>Enabled via DHCP</NetBIOS>
    <WindowsFirewallDisabled>True</WindowsFirewallDisabled>
  </IPInformation>
  <ServerInformation Display="Server Information">
    <HostName>Server123</HostName>
    <OS>Microsoft Windows Server 2008 R2 Enterprise 64-bit</OS>
    <OSVersion Display="OS Version">6.1.7601</OSVersion>
    <OSServicePack Display="Service Pack">Service Pack 1</OSServicePack>
    <CPUs>1</CPUs>
    <Memory>4096</Memory>
  </ServerInformation>
</QAReport>

我的 XSLT 看起来像这样

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" exclude-result-prefixes="xs xdt err fn" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" xmlns:err="http://www.w3.org/2005/xqt-errors">
    <xsl:output method="html" indent="yes"/>
    <xsl:template match="/">
        <table class="NVPair">
            <tr>
                <th>Report Date</th>
                <td>
                    <xsl:value-of select="/QAReport/@ReportDate"/>
                </td>
            </tr>
            <tr>
                <th>QA Status</th>
                <td>
                    <xsl:if test="/QAReport/@QAStatus = 2 ">
                        <span class="QAPass">
                            <xsl:value-of select="/QAReport/@QAStatus"/> (OK)</span>
                    </xsl:if>
                    <xsl:if test="/QAReport/@QAStatus != 2 ">
                        <span class="QAFail">
                            <xsl:value-of select="/QAReport/@QAStatus"/> (Failure)</span>
                    </xsl:if>
                </td>
            </tr>
        </table>
        <xsl:apply-templates select="QAReport/*"/>
    </xsl:template>

  <xsl:template match="/QAReport/*">
        <h3>
            <xsl:choose>
                <xsl:when test="@Display">
                    <xsl:value-of select="@Display"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="name(.)"/>
                </xsl:otherwise>
            </xsl:choose>
        </h3>
        <xsl:text>
</xsl:text>
        <xsl:if test="*/.[not(@Format)]"><!-- format each NVPair first -->
            <table class="NVPair">
                <xsl:for-each select="*/.[not(@Format='Table')]">
                    <xsl:call-template name="NVPair"/>
                </xsl:for-each>
            </table>
        </xsl:if>
        <xsl:if test="*/.[@Format='Table']"><!-- select each table in the section-->
            <xsl:call-template name="QATable"/>
        </xsl:if>
    </xsl:template>
    <xsl:template name="NVPair">
        <tr>
            <th>
                <xsl:value-of select="name(.)"/>
            </th>
            <td>
                <xsl:value-of select="."/>
            </td>
        </tr>
    </xsl:template>


<xsl:template name="QATable">
        <h4>
            <xsl:value-of select="name(*)"/>
        </h4>
        <table class="QATable">
            <xsl:for-each select="./*/*">
                <xsl:value-of select="name(.)"/>
                <xsl:choose>
                    <xsl:when test="position()=1">
                        <xsl:call-template name="QATableRowFirst"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:call-template name="QATableRow"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </table>
    </xsl:template>
    <xsl:template name="QATableRowFirst">
        <thead>
            <tr>
                <xsl:for-each select="./*">
                    <td>
                        <xsl:value-of select="name(.)"/>
                    </td>
                </xsl:for-each>
            </tr>
        </thead>
        <tr>
            <xsl:for-each select="./*">
                <td>
                    <xsl:value-of select="."/>
                </td>
            </xsl:for-each>
        </tr>
    </xsl:template>
    <xsl:template name="QATableRow">
        <tr>
            <xsl:for-each select="./*">
                <td>
                    <xsl:value-of select="."/>
                </td>
            </xsl:for-each>
        </tr>
    </xsl:template>
</xsl:stylesheet>

我有几个问题。

1) 考虑到 NVP 部分是动态的,是否有一种很好的方法可以将第一个 xsl:for-each 循环用作模板。

2) Format='Table' 模板非​​常难看 - 两个 for-eaches,一个用于行,一个用于列 - 调用模板或应用模板有更好的方法吗?

请记住,在任何时候 XML 都可以有不同的部分,而表虽然在每个部分中是一致的,但可以有任意数量的列。

更新(2015.05-13):

基于@Michael.hor257k,我最终得到了这个,它工作得更好,更简单。

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="2.0" exclude-result-prefixes="xs xdt err fn" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" xmlns:err="http://www.w3.org/2005/xqt-errors">
  <xsl:output method="html" indent="yes"/>
  <xsl:template match="/">
    <link rel="stylesheet" type="text/css" href="Styles/QAReportStyle.css"/>
    <table class="NVPair">...</table>
    <xsl:apply-templates select="/QACheck/*"/>
  </xsl:template>
  <xsl:template match="/QACheck/*">
    <h3><xsl:value-of select="name(.)"/></h3>
    <xsl:choose>
      <xsl:when test="*[not(@Format)]">
        <!-- format each NVPair first -->
        <table class="NVPair">
          <xsl:for-each select="*[not(@Format='Table')]">
            <xsl:call-template name="NVPair"/>
          </xsl:for-each>
        </table>
      </xsl:when>
      <xsl:otherwise>
        <!-- select each table in the section-->
        <xsl:apply-templates select="./*[@Format='Table']"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template name="NVPair">
    <tr>
      <th><xsl:value-of select="name(.)</th>
      <td><xsl:value-of select="."/></td>
    </tr>
  </xsl:template>
  <xsl:template name="QATable" match="*[@Format='Table']">
    <h4><xsl:value-of select="name(.)</h4>
    <xsl:choose>
      <xsl:when test="count(*) = 0">
        <p>No Rows</p>
      </xsl:when>
      <xsl:otherwise>
        <table class="QATable">
          <thead>
            <tr>
            <xsl:for-each select="*[1]/*">
              <td><xsl:value-of select="name(.)"/></td>
            </xsl:for-each>
            </tr>
          </thead>
          <xsl:for-each select="*">
            <tr>
              <xsl:for-each select="*">
                <td><xsl:value-of select="."/></td>
              </xsl:for-each>
            </tr>
          </xsl:for-each>
        </table>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    这样的东西对你有用吗?

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="/*">
        <html>
            <body>
                <xsl:apply-templates select="*/*[@Format='Table']"/>
            </body>
        </html>
    </xsl:template>
    
    <xsl:template match="*[@Format='Table']">
        <table border="1">
            <tr>
                <xsl:for-each select="*[1]/*">
                    <th>
                        <xsl:value-of select="name()"/>
                    </th>
                </xsl:for-each>
            </tr>           
            <xsl:for-each select="*">
            <tr>
                <xsl:for-each select="*">
                    <td>
                        <xsl:value-of select="."/>
                    </td>
                </xsl:for-each>
            </tr>
            </xsl:for-each>
        </table>
    </xsl:template>
    
    </xsl:stylesheet>
    

    注意事项

    1. 假设所有表都处于同一层次;如果不是这样,请改用(效率较低的)指令&lt;xsl:apply-templates select=".//*[@Format='Table']"/&gt;

    2. 假设所有表的层次结构相同;

    3. 有两条xsl:for-each指令并没有错;内容是如此琐碎,以至于将它们拆分为单独的模板没有任何用处(除了降低代码的可读性)。

    【讨论】:

    • 这无疑让我朝着正确的方向前进。这有点奇怪,因为您删除了一层模板,而我的 XML 测试工具最终的工作方式与 asp.net 略有不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    相关资源
    最近更新 更多