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

如何用XSLT 1.0实现动态列并映射行值到列头(续)

问题:使用XSLT 1.0针对不同时间列生成两个指定格式表格

需求说明

需要基于给定的XML数据,仅使用XSLT 1.0生成以下两个格式的HTML表格:

预期输出1:Ribbon Rezept表格

<p>Ribbon Rezept</p>
<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th>Medikament</th>
<th>Dosis</th>
<th>08:00</th>
<th>12:00</th>
<th>18:00</th>
<th>22:00</th>
</tr>
</thead>
<tbody>
<tr>
<td>ASPIRIN</td>
<td>0-0-0-1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
</div>

预期输出2:Ribbon Medikarte表格

<p>Ribbon Medikarte</p>
<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th>Medikament</th>
<th>Dosis</th>
<th>08:00</th>
<th>12:00</th>
<th>16:00</th>
<th>20:00</th>
<th>22:00</th>
</tr>
</thead>
<tbody>
<tr>
<td>ASPIRIN</td>
<td>0-0-0-1</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td></td>
</tr>
<tr>
<td>PONSTAN</td>
<td>1-0-0-1</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td></td>
<td>1</td>
</tr>
</tbody>
</table>
</div>

源XML数据

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl"?>
<DATA>

<RIBBON_REZEPT>
<SRO_INT_REZEPT_INFO>
  <MRO_INT_REZEPT>
    <MEDIKAMENT_M CR="1">ASPIRIN</MEDIKAMENT_M>
    <DOSIS_M CR="1">0-0-0-1</DOSIS_M>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>08:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>12:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>18:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>1</DOSIS_N>
      <ZEIT_T>22:00</ZEIT_T>
    </EINZELDOSIS>
    <EINHEIT_M CR="1">Stk</EINHEIT_M>
  </MRO_INT_REZEPT>
</SRO_INT_REZEPT_INFO>
</RIBBON_REZEPT>


<RIBBON_MEDIKARTE>
<SRO_INT_REZEPT_INFO>
  <MRO_INT_REZEPT>
    <MEDIKAMENT_M CR="1">ASPIRIN</MEDIKAMENT_M>
    <DOSIS_M CR="1">0-0-0-1</DOSIS_M>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>08:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>12:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>16:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>1</DOSIS_N>
      <ZEIT_T>20:00</ZEIT_T>
    </EINZELDOSIS>
    <EINHEIT_M CR="1">Stk</EINHEIT_M>
  </MRO_INT_REZEPT>
  <MRO_INT_REZEPT>
    <MEDIKAMENT_M CR="1">PONSTAN</MEDIKAMENT_M>
    <DOSIS_M CR="1">1-0-0-1</DOSIS_M>
    <EINZELDOSIS>
      <DOSIS_N>1</DOSIS_N>
      <ZEIT_T>08:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>12:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>0</DOSIS_N>
      <ZEIT_T>16:00</ZEIT_T>
    </EINZELDOSIS>
    <EINZELDOSIS>
      <DOSIS_N>1</DOSIS_N>
      <ZEIT_T>22:00</ZEIT_T>
    </EINZELDOSIS>
    <EINHEIT_M CR="1">Stk</EINHEIT_M>
  </MRO_INT_REZEPT>
</SRO_INT_REZEPT_INFO>
</RIBBON_MEDIKARTE>

</DATA>

当前问题

尝试使用以下XSLT代码生成表格,但由于RIBBON_REZEPT和RIBBON_MEDIKARTE中的时间节点不同,无法得到预期格式:

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

    <xsl:output encoding="ISO-8859-1" indent="yes" method="html"/>

   <xsl:key match="EINZELDOSIS" name="data-by-time" use="ZEIT_T"/>
   <xsl:key match="EINZELDOSIS" name="data-by-cell" use="concat(ZEIT_T, '|', generate-id(..))"/>


<xsl:template match="/">
    <html>
    <head charset="ISO-8859-1">
          <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
          <title>Medilist v2</title>
    </head>
    <body>

    <p>Ribbon Rezept</p>
    <xsl:call-template name="RIBBON">
       <xsl:with-param name="medinode" select="/DATA/RIBBON_REZEPT/SRO_INT_REZEPT_INFO"/>
    </xsl:call-template>

    <p>Ribbon Medikarte</p>
    <xsl:call-template name="RIBBON">
       <xsl:with-param name="medinode" select="/DATA/RIBBON_MEDIKARTE/SRO_INT_REZEPT_INFO"/>
    </xsl:call-template>
    </body>
    </html>

</xsl:template>

<xsl:template name="RIBBON">
    <xsl:param name="medinode" />

   <xsl:variable name="distinct-times-RTF">
           <xsl:for-each select="$medinode/MRO_INT_REZEPT/EINZELDOSIS[count(. | key('data-by-time', ZEIT_T)[1]) = 1]">
 
           <xsl:sort data-type="text" order="ascending" select="ZEIT_T"/>
               <xsl:copy-of select="ZEIT_T"/>
           </xsl:for-each>
   </xsl:variable>
    
   <xsl:variable name="distinct-times" select="ext:node-set($distinct-times-RTF)/ZEIT_T"/>

   <xsl:variable name="source_document" select="/"/>

    <table border="1">
        <thead>
            <tr>
                <th>Medi</th>
                <th>Dosis</th>
                <!-- a column header for each distinct time point -->
                <xsl:for-each select="$distinct-times">
                    <th>
                        <xsl:value-of select="."/>
                    </th>
                </xsl:for-each>
            </tr>
        </thead>
        <tbody>
            <xsl:for-each select="$medinode/MRO_INT_REZEPT">
                <xsl:variable name="row-id" select="generate-id()"/>
                <tr>
                    <td>
                        <xsl:value-of select="MEDIKAMENT_M"/>
                    </td>
                    <td>
                        <xsl:value-of select="DOSIS_M"/>
                    </td>

                    <!-- create a cell for each distinct time point -->
                    <xsl:for-each select="$distinct-times">
                        <xsl:variable name="zeit" select="."/>
                        <td>
                            <!-- get matching data point -->
                            <!-- switch the context back to the source document -->
                            <xsl:for-each select="$source_document">
                                <xsl:value-of select="key('data-by-cell', concat($zeit, '|', $row-id))/DOSIS_N"/>
                            </xsl:for-each>
                        </td>
                    </xsl:for-each>
                </tr>
            </xsl:for-each>
        </tbody>
    </table>
</xsl:template>

</xsl:stylesheet>

解决方案

问题出在全局定义的xsl:key会匹配整个文档的EINZELDOSIS节点,导致提取各自的时间列时出现混淆。修改为针对当前处理的节点范围生成局部时间列,同时调整数据匹配方式:

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

    <xsl:output encoding="ISO-8859-1" indent="yes" method="html"/>

    <xsl:template match="/">
        <html>
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
                <title>Medilist v2</title>
            </head>
            <body>
                <p>Ribbon Rezept</p>
                <xsl:call-template name="RIBBON">
                    <xsl:with-param name="medinode" select="/DATA/RIBBON_REZEPT/SRO_INT_REZEPT_INFO"/>
                </xsl:call-template>

                <p>Ribbon Medikarte</p>
                <xsl:call-template name="RIBBON">
                    <xsl:with-param name="medinode" select="/DATA/RIBBON_MEDIKARTE/SRO_INT_REZEPT_INFO"/>
                </xsl:call-template>
            </body>
        </html>
    </xsl:template>

    <xsl:template name="RIBBON">
        <xsl:param name="medinode" />

        <!-- 提取当前模块下的所有时间并去重排序 -->
        <xsl:variable name="distinct-times-RTF">
            <xsl:for-each select="$medinode/MRO_INT_REZEPT/EINZELDOSIS">
                <xsl:sort data-type="text" order="ascending" select="ZEIT_T"/>
                <xsl:if test="count(. | $medinode/MRO_INT_REZEPT/EINZELDOSIS[ZEIT_T = current()/ZEIT_T][1]) = 1">
                    <zeit><xsl:value-of select="ZEIT_T"/></zeit>
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>
        <xsl:variable name="distinct-times" select="ext:node-set($distinct-times-RTF)/zeit"/>

        <div class="s-table-container">
            <table class="s-table">
                <thead>
                    <tr>
                        <th>Medikament</th>
                        <th>Dosis</th>
                        <xsl:for-each select="$distinct-times">
                            <th><xsl:value-of select="."/></th>
                        </xsl:for-each>
                    </tr>
                </thead>
                <tbody>
                    <xsl:for-each select="$medinode/MRO_INT_REZEPT">
                        <xsl:variable name="current-rezept" select="."/>
                        <tr>
                            <td><xsl:value-of select="MEDIKAMENT_M"/></td>
                            <td><xsl:value-of select="DOSIS_M"/></td>
                            <xsl:for-each select="$distinct-times">
                                <td>
                                    <xsl:value-of select="$current-rezept/EINZELDOSIS[ZEIT_T = current()]/DOSIS_N"/>
                                </td>
                            </xsl:for-each>
                        </tr>
                    </xsl:for-each>
                </tbody>
            </table>
        </div>
    </xsl:template>

</xsl:stylesheet>

修改说明

  1. 移除全局键定义:不再使用全局xsl:key,改为在当前模块范围内直接查找匹配的时间节点,避免跨模块数据干扰。
  2. 局部时间去重:通过current()函数限定只在当前处理的medinode范围内去重时间值,确保每个表格的时间列完全匹配自身模块的数据。
  3. 匹配单元格数据:遍历时间列时,直接在当前药品节点下查找对应时间的剂量值,缺失时自动生成空单元格,符合预期格式。
  4. 添加HTML容器:补充了预期输出中的s-table-container和s-table类,确保结构完全匹配需求。

内容的提问来源于stack exchange,提问作者Laurent Wagner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 17:25:20