0

I have this 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>

This is my transformation code:

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;
}

I got this exception:

<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>

How can I prevent this issue?

1 Answer 1

2

Your XSLT has 151 lines. You would do well to remove blocks of instructions until you locate the problem (or at least the problematic area). In addition, some tools (e.g. https://xsltfiddle.liberty-development.net/) will provide a line number along with the error message.

Dumping your entire code here and expecting someone else to debug it is not the right thing to do. Moreover, it is impossible to debug it without having the XML input.

That said, I see this on lines 57 - 73 of your XSLT:

            <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>

Here you create a div element and populate it with some text (if one of the tests returns true). Then you try to add an attribute. This is not allowed, as your error message is trying to tell you:

Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added.

This conforms to the XSLT specification:

The following are all errors:

  • Adding an attribute to an element after children have been added to it;

I did not check your code beyond this point. There may be more similar or other errors.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. I take on your points. So, if it is not allowed then what is the resolution? I understood you could do that kind of thing because it works in the I.E. browser.
In the given example, all you need to do is move the xsl:attribute instruction so that it is the first child of div (before xsl:choose).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.