【问题标题】:Exception when transforming XML to HTML using C#使用 C# 将 XML 转换为 HTML 时出现异常
【发布时间】:2019-06-09 15:13:07
【问题描述】:

我有这个 XML:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" indent="yes" version="4.01"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    doctype-public="//W3C//DTD XHTML 1.0 Transitional//EN"/>
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <xsl:attribute name="lang">
        <xsl:value-of select="//Settings//LanguageCode"/>
      </xsl:attribute>
      <head>
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
        <link rel="stylesheet" type="text/css">
          <xsl:attribute name="href">
            <xsl:value-of select="//Settings/Template/@Style"/>
          </xsl:attribute>
        </link>
        <title>
          <!--<xsl:value-of select="//Labels/ReportTitleWorksheets"/>-->
          Assignment Slips
        </title>
        <style type="text/css">
          @import url('<xsl:text>2019/</xsl:text><xsl:value-of select="//Settings/Template/@Style"/>');
        </style>
      </head>
      <body>
        <xsl:for-each select="AssignmentSlips/Page">
          <div class="containerPage">
            <xsl:if test="@PageBreakBefore=1">
              <br style="page-break-before: always;"/>
            </xsl:if>
            <xsl:for-each select="StudentSlip">
              <div class="containerSlip">
                <img alt="s89" width="323px" height="429px">
                  <xsl:attribute name="src">
                    <xsl:text>2019\</xsl:text>
                    <xsl:value-of select="//Settings/Template"/>
                  </xsl:attribute>
                </img>
                <div class="fieldName">
                  <xsl:attribute name="dir">
                    <xsl:value-of select="//Settings/Direction"/>
                  </xsl:attribute>
                  <xsl:value-of select="Student"/>
                </div>
                <div class="fieldAssisant">
                  <xsl:attribute name="dir">
                    <xsl:value-of select="//Settings/Direction"/>
                  </xsl:attribute>
                  <xsl:value-of select="Assistant"/>
                </div>
                <div class="fieldDate">
                  <xsl:attribute name="dir">
                    <xsl:value-of select="//Settings/Direction"/>
                  </xsl:attribute>
                  <xsl:value-of select="Date"/>
                </div>
                <div class="fieldCounsel">
                  <xsl:choose>
                    <xsl:when test="@ItemPosition='1'">
                      <xsl:text>1st: </xsl:text>
                    </xsl:when>
                    <xsl:when test="@ItemPosition='2'">
                      <xsl:text>2nd: </xsl:text>
                    </xsl:when>
                    <xsl:when test="@ItemPosition='3'">
                      <xsl:text>3rd: </xsl:text>
                    </xsl:when>
                  </xsl:choose>
                  <xsl:attribute name="dir">
                    <xsl:value-of select="//Settings/Direction"/>
                  </xsl:attribute>
                  <xsl:value-of select="StudyPoint"/>
                </div>

                <xsl:choose>
                  <xsl:when test="Assignment=1">
                    <div class="checkBibleReading">✓</div>
                  </xsl:when>
                  <xsl:when test="Assignment=2">
                    <div class="checkInitialCall">✓</div>
                    <div class="fieldInitialCallIndex">
                      <xsl:choose>
                        <xsl:when test="@ItemPosition='1'">
                          <xsl:text>#1</xsl:text>
                        </xsl:when>
                        <xsl:when test="@ItemPosition='2'">
                          <xsl:text>#2 </xsl:text>
                        </xsl:when>
                        <xsl:when test="@ItemPosition='3'">
                          <xsl:text>#3</xsl:text>
                        </xsl:when>
                      </xsl:choose>
                    </div>
                  </xsl:when>
                  <xsl:when test="Assignment=3">
                    <div class="checkFirstReturnVisit">✓</div>
                    <div class="fieldFirstReturnVisitIndex">
                      <xsl:choose>
                        <xsl:when test="@ItemPosition='1'">
                          <xsl:text>#1</xsl:text>
                        </xsl:when>
                        <xsl:when test="@ItemPosition='2'">
                          <xsl:text>#2 </xsl:text>
                        </xsl:when>
                        <xsl:when test="@ItemPosition='3'">
                          <xsl:text>#3</xsl:text>
                        </xsl:when>
                      </xsl:choose>
                    </div>
                  </xsl:when>
                  <xsl:when test="Assignment=4">
                    <div class="checkSecondReturnVisit">✓</div>
                  </xsl:when>
                  <xsl:when test="Assignment=5">
                    <div class="checkThirdReturnVisit">✓</div>
                  </xsl:when>
                  <xsl:when test="Assignment=6">
                    <div class="checkBibleStudy">✓</div>
                  </xsl:when>
                  <xsl:when test="Assignment=7">
                    <div class="checkTalk">✓</div>
                  </xsl:when>
                  <xsl:when test="Assignment=0">
                    <div class="checkOther">✓</div>
                    <div class="fieldOther">
                      <xsl:attribute name="dir">
                        <xsl:value-of select="//Settings/Direction"/>
                      </xsl:attribute>
                      <xsl:value-of select="Other"/>
                    </div>
                  </xsl:when>
                </xsl:choose>
                <xsl:choose>
                  <xsl:when test="Location=1">
                    <div class="checkMainHall">✓</div>
                  </xsl:when>
                  <xsl:when test="Location=2">
                    <div class="checkAuxClass1">✓</div>
                  </xsl:when>
                  <xsl:when test="Location=3">
                    <div class="checkAuxClass2">✓</div>
                  </xsl:when>
                </xsl:choose>
              </div>
            </xsl:for-each>
          </div>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

这是我的转换代码:

public bool TransformXMLToHTML(string strTransformXSLPath, string strScheduleXMLPath, string strScheduleHTMLPath)
{
    try
    {
        var xmlResolver = new XmlUrlResolver();
        XsltArgumentList argsList = new XsltArgumentList();

        string strRootPath = Path.GetDirectoryName(strTransformXSLPath);

        // Read the XSL file and locate all the CSS documents that are used
        int iFileCount = 0;
        string[] lines = File.ReadAllLines(strTransformXSLPath);
        foreach (string line in lines)
        {
            if ((line).Trim().Contains("text/css"))
            {
                int iHREFIndex = line.IndexOf("href=\"");
                if (iHREFIndex != -1)
                {
                    string strCSSFile = line.Substring(iHREFIndex + 6);

                    int iQuoteIndex = strCSSFile.IndexOf("\"");
                    if (iQuoteIndex != -1)
                    {
                        strCSSFile = strCSSFile.Substring(0, iQuoteIndex);

                        // Build full path and make sure the file exists
                        string strCSSFilePath = Path.Combine(strRootPath, strCSSFile);
                        if(File.Exists(strCSSFilePath))
                        {
                            // Establish the parameter name
                            iFileCount++;
                            string strParamName = "CSSFile" + iFileCount.ToString();

                            // Read the content and attach
                            string strCSSFileContent = File.ReadAllText(strCSSFilePath);
                            argsList.AddParam(strParamName, "", strCSSFileContent);
                        }
                    }
                }
            }
        }

        // Now perform the transformation
        XslCompiledTransform transformer = new XslCompiledTransform();
        transformer.Load(strTransformXSLPath, new XsltSettings { EnableDocumentFunction = true }, xmlResolver);

        using (StreamWriter sw = new StreamWriter(strScheduleHTMLPath))
        {
            transformer.Transform(strScheduleXMLPath, argsList, sw);
        }
    }
    catch (Exception ex)
    {
        SimpleLog.Log(ex);
        return false;
    }

    return true;
}

我遇到了这个异常:

<LogEntry Date="2019-06-09 16:04:59" Severity="Exception" Source="MSAToolsLibrary.MSAToolsLibraryClass.TransformXMLToHTML" ThreadId="1">
  <Exception Type="System.Xml.Xsl.XslTransformException" Source="System.Xml.Xsl.Runtime.XmlQueryOutput.ThrowInvalidStateError">
    <Message>Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added.</Message>
    <StackTrace>   at System.Xml.Xsl.Runtime.XmlQueryOutput.ThrowInvalidStateError(XPathNodeType constructorType)
   at System.Xml.Xsl.Runtime.XmlQueryOutput.ConstructInEnumAttrs(XPathNodeType rootType)
   at System.Xml.Xsl.Runtime.XmlQueryOutput.WriteStartAttribute(String prefix, String localName, String ns)
   at &lt;xsl:template name="compiler:generated"&gt;(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, XPathNavigator {urn:schemas-microsoft-com:xslt-debug}current)
   at &lt;xsl:template match="/"&gt;(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, XPathNavigator {urn:schemas-microsoft-com:xslt-debug}current)
   at Root(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime)
   at Execute(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime)
   at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlSequenceWriter results)
   at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer)
   at System.Xml.Xsl.XslCompiledTransform.Transform(String inputUri, XsltArgumentList arguments, TextWriter results)
   at MSAToolsLibrary.MSAToolsLibraryClass.TransformXMLToHTML(String strTransformXSLPath, String strScheduleXMLPath, String strScheduleHTMLPath)</StackTrace>
  </Exception>
</LogEntry>

我怎样才能避免这个问题?

【问题讨论】:

    标签: c# xml xslt-1.0


    【解决方案1】:

    您的 XSLT 有 151 行。您最好删除指令块,直到您找到问题(或至少是有问题的区域)。此外,一些工具(例如https://xsltfiddle.liberty-development.net/)将提供行号以及错误消息。

    将您的整个代码转储到此处并期望其他人对其进行调试是不正确的做法。此外,没有 XML 输入是无法调试的。

    也就是说,我在 XSLT 的第 57 - 73 行看到了这一点:

                <div class="fieldCounsel">
                  <xsl:choose>
                    <xsl:when test="@ItemPosition='1'">
                      <xsl:text>1st: </xsl:text>
                    </xsl:when>
                    <xsl:when test="@ItemPosition='2'">
                      <xsl:text>2nd: </xsl:text>
                    </xsl:when>
                    <xsl:when test="@ItemPosition='3'">
                      <xsl:text>3rd: </xsl:text>
                    </xsl:when>
                  </xsl:choose>
                  <xsl:attribute name="dir">
                    <xsl:value-of select="//Settings/Direction"/>
                  </xsl:attribute>
                  <xsl:value-of select="StudyPoint"/>
                </div>
    

    在这里您创建一个div 元素并用一些文本填充它(如果其中一个测试返回true)。然后你尝试添加一个属性。这是不允许的,因为您的错误消息试图告诉您:

    在文本、注释、pi 或子元素节点已添加后,不能将属性和命名空间节点添加到父元素。

    这符合XSLT specification:

    以下都是错误:

    • 在向元素添加子元素后向元素添加属性;

    在此之后,我没有检查您的代码。可能还有更多类似或其他错误。

    【讨论】:

    • 谢谢。我接受你的观点。那么,如果不允许,那么解决方案是什么?我知道你可以做那种事情,因为它适用于 I.E.浏览器。
    • 在给定的示例中,您需要做的就是移动xsl:attribute 指令,使其成为div 的第一个孩子(在xsl:choose 之前)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多