如何用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>
修改说明
- 移除全局键定义:不再使用全局
xsl:key,改为在当前模块范围内直接查找匹配的时间节点,避免跨模块数据干扰。 - 局部时间去重:通过
current()函数限定只在当前处理的medinode范围内去重时间值,确保每个表格的时间列完全匹配自身模块的数据。 - 匹配单元格数据:遍历时间列时,直接在当前药品节点下查找对应时间的剂量值,缺失时自动生成空单元格,符合预期格式。
- 添加HTML容器:补充了预期输出中的
s-table-container和s-table类,确保结构完全匹配需求。
内容的提问来源于stack exchange,提问作者Laurent Wagner
相关产品推荐
相关产品推荐

