I'm attempting to perform a very simply transform of Timed Text Markup Language (TTML) documents. Here's a minimalist example of a TTML file:
<?xml version="1.0" encoding="UTF-8"?>
<tt xml:lang="en" xmlns="http://www.w3.org/2006/04/ttaf1"
xmlns:tts="http://www.w3.org/2006/04/ttaf1#styling">
<head>
</head>
<body>
<div xml:lang="en" style="1">
<p begin="00:00:00.20" dur="00:00:02.26">>> One time entry<br/>with a line break.</p>
</div>
</body>
</tt>
Note the document's default namespace. That's key to the problem I'm having.
Here is the transform I'm using. This is the whole thing, it's very simple.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
xmlns:tt="http://www.w3.org/2006/04/ttaf1">
<xsl:output method="text" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:preserve-space elements="tt:p"/>
<!-- The indentation of the close tag for the following node is crucial to the transformed layout. Don't move it! -->
<xsl:template match="tt:p"><xsl:apply-templates /> 
</xsl:template>
<xsl:template match="tt:p/text()"><xsl:copy /> </xsl:template>
</xsl:stylesheet>
Our dataset has hundreds of documents and they don't all have the same default namespace. However, as you can see from the above XSLT, the transform is simple and based on the core data element <p />
so ultimately the namespace variations are unimportant.
The above XSL currently works with some of the source files since the XSLT file has an explicitly defined namespace prefix (tt
). However, this doesn't work when encountering another source file with a different default namespace.
So I need to find a way to provide the XSLT with an arbitrary, unknown namespace value for a known prefix. I have code that can be used to find the source document default namespace:
XPathDocument sourceDoc = new XPathDocument("sourcefile.xml");
XPathNavigator sourceNav = sourceDoc.CreateNavigator();
sourceNav.MoveToFollowing(XPathNodeType.Element);
var sourceNS = sourceNav.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml);
string defNS = sourceNS.Single(n => n.Key =="").Value;
This correctly gives me http://www.w3.org/2006/04/ttaf1
. However, I can't seem to figure out what to do with this value. I have searched and experimented for many hours trying to somehow provide the XslCompiledTransform
instance the variable namespace. It appears that there's nothing within the area of the transform itself that can take it.
I've tried explicitly loading the XSLT file into an XmlDocument
in order to manipulate the name table (after removing the explicit xmlns:tt="..."
namespace declaration in the above XSLT):
XslCompiledTransform objXsl = new XslCompiledTransform();
StringWriter writer = new StringWriter();
XPathDocument sourceDoc;
XmlDocument xslDoc = new XmlDocument();
XPathNavigator sourceNav, xslNav;
XmlNamespaceManager xslNsManager;
sourceDoc = new XPathDocument("sourcefile.xml");
sourceNav = sourceDoc.CreateNavigator();
sourceNav.MoveToFollowing(XPathNodeType.Element);
var sourceNS = sourceNav.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml);
xslDoc.Load("transform.xslt");
xslNsManager = new XmlNamespaceManager(xslDoc.NameTable);
xslNsManager.AddNamespace("tt", sourceNS.Single(n => n.Key =="").Value);
xslNav = xslDoc.CreateNavigator();
objXsl.Load(xslNav);
objXsl.Transform(sourceNav, null, writer);
This runs up until the actual transform, where I get an XslLoadException
stating that Prefix 'tt' is not defined.
I'm at a loss at this point. Everything I can find from searching discusses putting the namespace into the XSLT document (which I already have and it works fine for one namespace value). I can't find anything in MSDN documentation or elsewhere that explains how to add the namespace definitions into the transform on the fly.
Anyone have any ideas?