Switch from XslTransform to XslCompiledTransform breaks API Data Page HTML output

We have an API Data Page generating HTML page as in the image below. The XSLT transformation has XsltEngine set as XslTransform. When we switch it to XslCompiledTransform (which is automatic from version 2025.3), the HTML page stops to work.

The transformation looks like this…

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
	xmlns:AS="http://schema.advantages.cz/AsapFunctions"
	xmlns:date="http://exslt.org/dates-and-times" exclude-result-prefixes="AS date">
	
	<xsl:template match="ROOT">
	<ROOT>
		<html>
			<head>
				<title>A</title>
				<script src="../../ttdcust/js/jquery-1.9.1.min.js" />
				<script src="../../ttdcust/js/apiSubstrateOutput.js" />
				<script src="../../ttdcust/js/jquery-ui.min.js" />
				<script src="../../ttdcust/js/jquery-ui-timepicker-addon.js" />
				<script src="../../ttdcust/js/jquery.dataTables.min.js" />
				<script src="../../ttdcust/js/dataTables.bootstrap.min.js" />
				
				<link rel="stylesheet" href="../../ttdcust/css/jquery-ui.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/substrateOutput.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/jquery-ui-timepicker-addon.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/bootstrap.min.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/dataTables.bootstrap.min.css" />
				
			</head>
			<body>
				<h1>Dodavatel Tereos TTD, a.s.</h1>
			</body>
		</html>
	</ROOT>
	</xsl:template>
</xsl:stylesheet>

The transformation result in Architect looks like this:

<?xml version="1.0" encoding="utf-8"?>
<ROOT>
  <html>
    <head>
      <title>A</title>
      <script src="../../ttdcust/js/jquery-1.9.1.min.js" />
      <script src="../../ttdcust/js/apiSubstrateOutput.js" />
      <script src="../../ttdcust/js/jquery-ui.min.js" />
      <script src="../../ttdcust/js/jquery-ui-timepicker-addon.js" />
      <script src="../../ttdcust/js/jquery.dataTables.min.js" />
      <script src="../../ttdcust/js/dataTables.bootstrap.min.js" />
      <link rel="stylesheet" href="../../ttdcust/css/jquery-ui.css" />
      <link rel="stylesheet" type="text/css" href="../../ttdcust/css/substrateOutput.css" />
      <link rel="stylesheet" type="text/css" href="../../ttdcust/css/jquery-ui-timepicker-addon.css" />
      <link rel="stylesheet" type="text/css" href="../../ttdcust/css/bootstrap.min.css" />
      <link rel="stylesheet" type="text/css" href="../../ttdcust/css/dataTables.bootstrap.min.css" />
    </head>
    <body>
      <h1>Dodavatel Tereos TTD, a.s.</h1>
    </body>
  </html>
</ROOT>

The output in browser depends on engine type of transformation.
If the transformation engine is XslTransform the html source in browser looks like this :


You can see the <head>, <body>, <root> elements are in different places, but the code syntax is correct. The page is shown in the browser.

If the transformation engine is XslCompiledTransform the html source in browser looks like this :


You can see the <head>, <body>, <root> elements are in different places as well, but there are also extra end elements and the code syntax is incorrect - see the arrows in the picture. The page is NOT shown in the browser - blank page.
The XslTransform result is from version 2024.7.0.3446
The XslCompiledTransform result is from version 2025.3.0.3759

You need to adjust the transformation to exclude the XML declaration from the output. For more info, see here. The topic discusses the same problem.

I modified the transformation with <xsl:output> like this :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
	xmlns:AS="http://schema.advantages.cz/AsapFunctions"
	xmlns:date="http://exslt.org/dates-and-times" exclude-result-prefixes="AS date">
	<xsl:output omit-xml-declaration="yes" />
	<xsl:template match="ROOT">
	<ROOT>
		<html>
			<head>
				<title>A</title>
				<script src="../../ttdcust/js/jquery-1.9.1.min.js" />
				<script src="../../ttdcust/js/apiSubstrateOutput.js" />
				<script src="../../ttdcust/js/jquery-ui.min.js" />
				<script src="../../ttdcust/js/jquery-ui-timepicker-addon.js" />
				<script src="../../ttdcust/js/jquery.dataTables.min.js" />
				<script src="../../ttdcust/js/dataTables.bootstrap.min.js" />
				
				<link rel="stylesheet" href="../../ttdcust/css/jquery-ui.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/substrateOutput.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/jquery-ui-timepicker-addon.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/bootstrap.min.css" />
				<link rel="stylesheet" type="text/css" href="../../ttdcust/css/dataTables.bootstrap.min.css" />
			</head>
			<body>
				<h1>Dodavatel Tereos TTD, a.s.</h1>
			</body>
		</html>
	</ROOT>
	</xsl:template>
</xsl:stylesheet>

This removed the XML declaration header, but the syntax errors in the code remain the same - see the picture below. The topic you mentioned - there is different MIME type - application/json. I have text/html. So there will be probably different issue.

Why do you have root element in the output html? This is not valid html element.

Without the root element it is not possible to save transformation in Architect. There is an error

The 'link' start tag on line 8 position 6 does not match the end tag of 'head'. Line 9, position 5.
Transformation result invalid.
The 'link' start tag on line 8 position 6 does not match the end tag of 'head'. Line 9, position 5.

That’s the transformation without root:

@washi I tried to omit the ROOT element in the transformation, but it generates an error in API response similar to the Architect transformation validator:

{
    "ClassName": "System.Exception",
    "Message": "The 'link' start tag on line 8 position 6 does not match the end tag of 'head'. Line 9, position 5.",
    "Data": {
        "logged": true
    },
    "InnerException": {
        "ClassName": "System.Exception",
        "Message": "Transformation result invalid.",
        "Data": null,
        "InnerException": {
            "ClassName": "System.Xml.XmlException",
            "Message": "The 'link' start tag on line 8 position 6 does not match the end tag of 'head'. Line 9, position 5.",
            "Data": null,
            "InnerException": null,
            "HelpURL": null,
            "StackTraceString": "   at System.Xml.XmlTextReaderImpl.Throw(Exception e)\r\n   at System.Xml.XmlTextReaderImpl.Throw(String res, String[] args)\r\n   at System.Xml.XmlTextReaderImpl.ThrowTagMismatch(NodeData startTag)\r\n   at System.Xml.XmlTextReaderImpl.ParseEndElement()\r\n   at System.Xml.XmlTextReaderImpl.ParseElementContent()\r\n   at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)\r\n   at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)\r\n   at System.Xml.XmlDocument.Load(XmlReader reader)\r\n   at Origam.Service.Core.XmlContainer.Load(XmlReader xmlReader, Boolean doProcessing)\r\n   at Origam.Rule.Xslt.CompiledXsltEngine.Transform(Object engine, XsltArgumentList xslArg, XPathDocument sourceXpathDoc, IXmlContainer resultDoc) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\CompiledXsltEngine.cs:line 72\r\n   at Origam.Rule.Xslt.MicrosoftXsltEngine.Transform(IXmlContainer data, Object xsltEngine, Hashtable parameters, String transactionId, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\MicrosoftXsltEngine.cs:line 184",
            "RemoteStackTraceString": null,
            "RemoteStackIndex": 0,
            "ExceptionMethod": null,
            "HResult": -2146232000,
            "Source": "System.Private.Xml",
            "WatsonBuckets": null,
            "res": "The '{0}' start tag on line {1} position {2} does not match the end tag of '{3}'.",
            "args": [
                "link",
                "8",
                "6",
                "head"
            ],
            "lineNumber": 9,
            "linePosition": 5,
            "sourceUri": "",
            "version": "2.0"
        },
        "HelpURL": null,
        "StackTraceString": "   at Origam.Rule.Xslt.MicrosoftXsltEngine.Transform(IXmlContainer data, Object xsltEngine, Hashtable parameters, String transactionId, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\MicrosoftXsltEngine.cs:line 216",
        "RemoteStackTraceString": null,
        "RemoteStackIndex": 0,
        "ExceptionMethod": null,
        "HResult": -2146233088,
        "Source": "Origam.Rule",
        "WatsonBuckets": null
    },
    "HelpURL": null,
    "StackTraceString": "   at Origam.Rule.Xslt.MicrosoftXsltEngine.Transform(IXmlContainer data, Object xsltEngine, Hashtable parameters, String transactionId, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\MicrosoftXsltEngine.cs:line 234\r\n   at Origam.Rule.Xslt.AbstractXsltEngine.Transform(IXmlContainer data, Guid transformationId, Guid retransformationId, Hashtable parameters, String transactionId, Hashtable retransformationParameters, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\AbstractXsltEngine.cs:line 163\r\n   at Origam.Server.Pages.XsltPageRequestHandler.Execute(AbstractPage page, Dictionary`2 parameters, IRequestWrapper request, IResponseWrapper response) in D:\\a\\1\\s\\backend\\Origam.Server\\Pages\\XsltPageRequestHandler.cs:line 159\r\n   at Origam.Server.Pages.UserApiProcessor.Process(IHttpContextWrapper context) in D:\\a\\1\\s\\backend\\Origam.Server\\Pages\\UserApiProcessor.cs:line 144",
    "RemoteStackTraceString": null,
    "RemoteStackIndex": 0,
    "ExceptionMethod": null,
    "HResult": -2146233088,
    "Source": "Origam.Rule",
    "WatsonBuckets": null
}

Any suggestion, how ho to resolve the issue with generating correct html code without legacy XslTransform engine?
Thank you.

Please, post the transformation as a code.

@washi
I looked at it in more detail and compared the outputs of the same transformation performed by different XSL engines. And finds 2 issues with XslCompiledTransform when generating HTML code.

  1. it requires <ROOT> element in the xsl transformation. There is an exception generated by API without <ROOT> element. But there is no definition for <ROOT> element in HTML.
  2. it generates code that wrongly replaces the closing element with self-closing tag - <tag attr="1"></tag> => <tag attr="1" /> for elements WITHOUT values. However, HTML does not know /> and the browser is unable to interpret these elements correctly. This is especially issue for elements like <script>, <link>, <input> and others that have an empty value. There is no definition for self-closing tags in HTML. It’s part of XHTML or XML. https://developer.mozilla.org/en-US/docs/Glossary/Void_element

So it would by great if the XslCompiledTransform can generate a valid HTML code as the XslTransform.

You can test it with transformation like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
	xmlns:AS="http://schema.advantages.cz/AsapFunctions"
	xmlns:date="http://exslt.org/dates-and-times" exclude-result-prefixes="AS date">
	<xsl:output omit-xml-declaration="yes"/>
	<xsl:template match="ROOT">
		<ROOT>
			<html>
				<head>
				    <title>Hello World Program</title>
				</head>
				<body>
				    <script src="test.js"></script>
				</body>
			</html>
		</ROOT>
	</xsl:template>
</xsl:stylesheet>

Script test.js is easy:

document.write('Hello World');

Output from XslTransform engine :

<!DOCTYPE html><?xml version="1.0" encoding="utf-8"?>
<ROOT>
  <html>
    <head>
      <title>Hello World Program</title>
    </head>
    <body>
      <script src="test.js">
      </script>
    </body>
  </html>
</ROOT>

The browse will correctly show Hello World even the <ROOT> is present.

Output from XslCompiledTransform engine :

<!DOCTYPE html>
<ROOT>
	<html>
		<head>
			<title>Hello World Program</title>
		</head>
		<body>
			<script src="test.js"/>
		</body>
	</html>
</ROOT>

The browse will show blank page.
The problem is the self-closing tag />

XslCompiledTransformation doesn’t require <ROOT> element. Please post the stack trace of the exception that is returned by API.

If I remove the ROOT element from the transformation mentioned above and XSL engine is set to XmlCompiledTranform the API returns exception:

{
	"ClassName": "System.Exception",
	"Message": "The 'META' start tag on line 3 position 6 does not match the end tag of 'head'. Line 5, position 5.",
	"Data": {
		"logged": true
	},
	"InnerException": {
		"ClassName": "System.Exception",
		"Message": "Transformation result invalid.",
		"Data": null,
		"InnerException": {
			"ClassName": "System.Xml.XmlException",
			"Message": "The 'META' start tag on line 3 position 6 does not match the end tag of 'head'. Line 5, position 5.",
			"Data": null,
			"InnerException": null,
			"HelpURL": null,
			"StackTraceString": "   at System.Xml.XmlTextReaderImpl.Throw(Exception e)\r\n   at System.Xml.XmlTextReaderImpl.Throw(String res, String[] args)\r\n   at System.Xml.XmlTextReaderImpl.ThrowTagMismatch(NodeData startTag)\r\n   at System.Xml.XmlTextReaderImpl.ParseEndElement()\r\n   at System.Xml.XmlTextReaderImpl.ParseElementContent()\r\n   at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)\r\n   at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)\r\n   at System.Xml.XmlDocument.Load(XmlReader reader)\r\n   at Origam.Service.Core.XmlContainer.Load(XmlReader xmlReader, Boolean doProcessing)\r\n   at Origam.Rule.Xslt.CompiledXsltEngine.Transform(Object engine, XsltArgumentList xslArg, XPathDocument sourceXpathDoc, IXmlContainer resultDoc) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\CompiledXsltEngine.cs:line 72\r\n   at Origam.Rule.Xslt.MicrosoftXsltEngine.Transform(IXmlContainer data, Object xsltEngine, Hashtable parameters, String transactionId, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\MicrosoftXsltEngine.cs:line 184",
			"RemoteStackTraceString": null,
			"RemoteStackIndex": 0,
			"ExceptionMethod": null,
			"HResult": -2146232000,
			"Source": "System.Private.Xml",
			"WatsonBuckets": null,
			"res": "The '{0}' start tag on line {1} position {2} does not match the end tag of '{3}'.",
			"args": [
				"META",
				"3",
				"6",
				"head"
			],
			"lineNumber": 5,
			"linePosition": 5,
			"sourceUri": "",
			"version": "2.0"
		},
		"HelpURL": null,
		"StackTraceString": "   at Origam.Rule.Xslt.MicrosoftXsltEngine.Transform(IXmlContainer data, Object xsltEngine, Hashtable parameters, String transactionId, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\MicrosoftXsltEngine.cs:line 216",
		"RemoteStackTraceString": null,
		"RemoteStackIndex": 0,
		"ExceptionMethod": null,
		"HResult": -2146233088,
		"Source": "Origam.Rule",
		"WatsonBuckets": null
	},
	"HelpURL": null,
	"StackTraceString": "   at Origam.Rule.Xslt.MicrosoftXsltEngine.Transform(IXmlContainer data, Object xsltEngine, Hashtable parameters, String transactionId, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\MicrosoftXsltEngine.cs:line 234\r\n   at Origam.Rule.Xslt.AbstractXsltEngine.Transform(IXmlContainer data, Guid transformationId, Guid retransformationId, Hashtable parameters, String transactionId, Hashtable retransformationParameters, IDataStructure outputStructure, Boolean validateOnly) in D:\\a\\1\\s\\backend\\Origam.Rule\\Xslt\\AbstractXsltEngine.cs:line 163\r\n   at Origam.Server.Pages.XsltPageRequestHandler.Execute(AbstractPage page, Dictionary`2 parameters, IRequestWrapper request, IResponseWrapper response) in D:\\a\\1\\s\\backend\\Origam.Server\\Pages\\XsltPageRequestHandler.cs:line 159\r\n   at Origam.Server.Pages.UserApiProcessor.Process(IHttpContextWrapper context) in D:\\a\\1\\s\\backend\\Origam.Server\\Pages\\UserApiProcessor.cs:line 144",
	"RemoteStackTraceString": null,
	"RemoteStackIndex": 0,
	"ExceptionMethod": null,
	"HResult": -2146233088,
	"Source": "Origam.Rule",
	"WatsonBuckets": null
}

But the exception complains about META tag. There’s no META element in the posted transformation nor in the posted result. It looks like that the exception is related to the different transformation.

I know there is no META tag. Similar error is shown directly in Architect. You can see there is no META tag in transformation, but error is shown for META tag. This happens when I remove ROOT element from transformation.

I see. This is behaviour is a bug and we’re going to address it.

After researching the issue the XslCompiledTransform decides that because the root element is html we need output as HTML formatted result including META tag as a bonus. To prevent this behaviour we need to resort to xsl:output to enforce xml output. So in your case the xsl:output should look like this:

<xsl:output omit-xml-declaration="yes" method="xml"/>