多层XML转嵌套HTML表格空表问题的修正方案咨询
问题描述
正在处理多层结构的XML数据,尝试转换为嵌套HTML表格展示。XML可能包含一层或多层信息,目前能显示内容,但存在多余的空表格问题。以下是使用的XML数据及XSLT模板,如何修正?
XML数据
<?xml version="1.0" encoding="UTF-8"?> <ErrorRecord> <Exception> <ErrorRecord>Exception calling "Fill" with "2" argument(s): "Divide by zero error encountered."</ErrorRecord> <WasThrownFromThrowStatement>True</WasThrownFromThrowStatement> <TargetSite>Void CheckActionPreference(System.Management.Automation.Language.FunctionContext, System.Exception)</TargetSite> <Message>Exception calling "Fill" with "2" argument(s): "Divide by zero error encountered."</Message> <Data>System.Collections.ListDictionaryInternal</Data> <InnerException> <Error> <Source>Core .Net SqlClient Data Provider</Source> <Number>8134</Number> <State>1</State> <Class>16</Class> <Server>.</Server> <Message>Divide by zero error encountered.</Message> <Procedure></Procedure> <LineNumber>1</LineNumber> </Error> <ClientConnectionId>cdfed373-14fb-4dd7-bebf-7158695cb2c7</ClientConnectionId> <Class>16</Class> <LineNumber>1</LineNumber> <Number>8134</Number> <Procedure></Procedure> <Server>.</Server> <State>1</State> <Source>Core .Net SqlClient Data Provider</Source> <IsTransient>False</IsTransient> <SqlState></SqlState> <BatchCommand></BatchCommand> <ErrorCode>-2146232060</ErrorCode> <TargetSite>Void OnError(System.Data.SqlClient.SqlException, Boolean, System.Action`1[System.Action])</TargetSite> <Message>Divide by zero error encountered.</Message> <Data>System.Collections.ListDictionaryInternal</Data> <InnerException></InnerException> <HelpLink></HelpLink> <HResult>-2146232060</HResult> <StackTrace> at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlDataReader.TryHasMoreRows(Boolean& moreRows) at System.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout, Boolean& more) at System.Data.SqlClient.SqlDataReader.Read() at System.Data.Common.DataAdapter.FillLoadDataRow(SchemaMapping mapping) at System.Data.Common.DataAdapter.FillFromReader(DataSet dataset, DataTable datatable, String srcTable, DataReaderContainer dataReader, Int32 startRecord, Int32 maxRecords, DataColumn parentChapterColumn, Object parentChapterValue) at System.Data.Common.DataAdapter.Fill(DataSet dataSet, String srcTable, IDataReader dataReader, Int32 startRecord, Int32 maxRecords) at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior) at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior) at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, String srcTable) at CallSite.Target(Closure, CallSite, Object, Object, Object)</StackTrace> </InnerException> <HelpLink></HelpLink> <Source>System.Management.Automation</Source> <HResult>-2146233087</HResult> <StackTrace> at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception) at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)</StackTrace> </Exception> </ErrorRecord>
原XSLT模板
<xsl:template match = "ErrorRecord"> <xsl:call-template name="ErrorTable"> <xsl:with-param name="n" select="." /> </xsl:call-template> </xsl:template> <xsl:template name="ErrorTable"> <xsl:param name="n" /> <xsl:for-each select="$n/*"> <table class="ErrorDetailTable"> <tr> <th class="Property"> </th> <th class="Value"> </th> </tr> <xsl:for-each select="*"> <xsl:choose> <xsl:when test="position() mod 2 = 0"> <tr class="tr-even"> <xsl:call-template name="ErrorCells"/> </tr> </xsl:when> <xsl:otherwise> <tr class="tr-odd"> <xsl:call-template name="ErrorCells"/> </tr> </xsl:otherwise> </xsl:choose> </xsl:for-each> <tr><td colspan="2"> </td></tr> </table> </xsl:for-each> </xsl:template> <xsl:template name="ErrorCells"> <td class="Property"><xsl:value-of select = "local-name(.)"/></td> <td class="Value"> <xsl:choose> <xsl:when test="count(./*) > 0"> <xsl:call-template name="ErrorTable"> <xsl:with-param name="n" select=".." /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:call-template name="replace_sab"> <xsl:with-param name="s" select="." /> <xsl:with-param name="a" select="'
'" /> <xsl:with-param name="b"><br /></xsl:with-param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </td> </xsl:template> <xsl:template name="replace_sab"> <!-- with string s, replace substring a by string b --> <!-- s, a and b are parameters determined upon calling --> <xsl:param name="s" /> <xsl:param name="a" /> <xsl:param name="b" /> <xsl:choose> <xsl:when test="contains($s,$a)"> <xsl:value-of select="substring-before($s,$a)" /> <xsl:copy-of select="$b" /> <xsl:call-template name="replace_sab"> <xsl:with-param name="s" select="substring-after($s,$a)" /> <xsl:with-param name="a" select="$a" /> <xsl:with-param name="b" select="$b" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$s" /> </xsl:otherwise> </xsl:choose> </xsl:template>
问题分析与修正方案
原XSLT出现空表格的核心原因有两点:
ErrorTable模板中,对$n/*循环时,无论当前节点是否有子节点都直接生成表格,导致空节点(如<InnerException></InnerException>)也会生成空表格。ErrorCells模板中调用ErrorTable时传递父节点(..),导致重复渲染父节点的所有子节点,引发冗余表格。
修正后的XSLT代码
<xsl:template match = "ErrorRecord"> <xsl:call-template name="ErrorTable"> <xsl:with-param name="n" select="." /> </xsl:call-template> </xsl:template> <xsl:template name="ErrorTable"> <xsl:param name="n" /> <!-- 仅当节点存在子节点时才生成表格 --> <xsl:if test="count($n/*) > 0"> <table class="ErrorDetailTable"> <tr> <th class="Property"> </th> <th class="Value"> </th> </tr> <xsl:for-each select="$n/*"> <xsl:choose> <xsl:when test="position() mod 2 = 0"> <tr class="tr-even"> <xsl:call-template name="ErrorCells"/> </tr> </xsl:when> <xsl:otherwise> <tr class="tr-odd"> <xsl:call-template name="ErrorCells"/> </tr> </xsl:otherwise> </xsl:choose> </xsl:for-each> <tr><td colspan="2"> </td></tr> </table> </xsl:if> </xsl:template> <xsl:template name="ErrorCells"> <td class="Property"><xsl:value-of select = "local-name(.)"/></td> <td class="Value"> <xsl:choose> <xsl:when test="count(./*) > 0"> <!-- 传递当前节点自身,避免重复渲染父节点内容 --> <xsl:call-template name="ErrorTable"> <xsl:with-param name="n" select="." /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:call-template name="replace_sab"> <xsl:with-param name="s" select="." /> <xsl:with-param name="a" select="'
'" /> <xsl:with-param name="b"><br /></xsl:with-param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </td> </xsl:template> <xsl:template name="replace_sab"> <!-- 替换字符串中的换行符为HTML换行标签 --> <xsl:param name="s" /> <xsl:param name="a" /> <xsl:param name="b" /> <xsl:choose> <xsl:when test="contains($s,$a)"> <xsl:value-of select="substring-before($s,$a)" /> <xsl:copy-of select="$b" /> <xsl:call-template name="replace_sab"> <xsl:with-param name="s" select="substring-after($s,$a)" /> <xsl:with-param name="a" select="$a" /> <xsl:with-param name="b" select="$b" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$s" /> </xsl:otherwise> </xsl:choose> </xsl:template>
关键修正点说明
- 消除空表格:在
ErrorTable模板开头添加<xsl:if test="count($n/*) > 0">判断,仅当节点存在子节点时才生成表格,空节点不再生成冗余表格。 - 修复嵌套逻辑:将
ErrorCells中调用ErrorTable的参数从..改为.,确保嵌套表格渲染当前节点的子节点,避免重复渲染父节点内容。
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

