如何在运行时向XslCompiledTransform添加任意名称空间?

时间:2011-10-25 19:40:45

标签: c# xml debugging xslt ttml

我正在尝试对Timed Text Markup Language(TTML)文档进行非常简单的转换。这是TTML文件的极简主义示例:

<?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">&gt;&gt; One time entry<br/>with a line break.</p>
        </div>
    </body>
</tt>

请注意文档的默认命名空间。这是我遇到的问题的关键。

这是我正在使用的转换。这就是全部,非常简单。

<?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 />&#160;
</xsl:template>

    <xsl:template match="tt:p/text()"><xsl:copy />&#160;</xsl:template>

</xsl:stylesheet>

我们的数据集包含数百个文档,并且它们并非都具有相同的默认命名空间。但是,正如您从上面的XSLT中看到的那样,转换很简单,并且基于核心数据元素<p />,因此最终命名空间变量并不重要。

上面的XSL目前可以处理一些源文件,因为XSLT文件有一个显式定义的名称空间前缀(tt)。但是,当遇到具有不同默认命名空间的另一个源文件时,这不起作用。

所以我需要找到一种方法来为XSLT提供已知前缀的任意未知名称空间值。我有可用于查找源文档默认命名空间的代码:

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;

这正确地给了我http://www.w3.org/2006/04/ttaf1。但是,我似乎无法弄清楚如何处理这个值。我已经搜索并试验了几个小时试图以某种方式为XslCompiledTransform实例提供变量名称空间。似乎变换本身的区域内没有任何东西可以接受它。

我已经尝试将XSLT文件显式加载到XmlDocument中以便操作名称表(在上面的XSLT中删除显式xmlns:tt="..."命名空间声明之后):

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

直到实际转换为止,我得到一个XslLoadException表示Prefix 'tt' is not defined.

我现在处于亏损状态。我从搜索中找到的所有内容都讨论了将命名空间放入XSLT文档(我已经拥有它并且它适用于一个命名空间值)。我在MSDN文档或其他地方找不到任何解释如何动态地将命名空间定义添加到转换中的内容。

有人有什么想法吗?

2 个答案:

答案 0 :(得分:1)

一位同事建议我只在XSL文档上添加/操作变量名称空间。我使用名称表更改的尝试朝向正确的方向,但它无法正常工作。以下是我根据他的建议提出的建议:

XslCompiledTransform xslXform = new XslCompiledTransform();
StringWriter writer = new StringWriter();
XmlDocument xslDoc = new XmlDocument();
XPathNavigator sourceNav;

sourceNav = new XPathDocument(sourceFile).CreateNavigator();
sourceNav.MoveToFollowing(XPathNodeType.Element);
var sourceNS = sourceNav.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml);
string ttNamespace = sourceNS.Single(n => n.Key == "").Value;

xslDoc.Load(xslFile);
xslDoc.DocumentElement.SetAttribute("xmlns:tt", ttNamespace);
xslXform.Load(xslDoc.CreateNavigator());
xslXform.Transform(sourceNav, null, writer);

这样可行,但对我来说感觉有些笨拙。我认为核心问题是我不理解(Xml | XPathDocument类型与与之关联的名称表/命名空间管理器之间的关系。

答案 1 :(得分:0)

我试试,这项工作。

using System;
using System.Xml.Xsl;

class Sample {
    static public void Main(){
        XslCompiledTransform xslt = new XslCompiledTransform();
        xslt.Load("transform.xslt");
        xslt.Transform("sourcefile.xml", "result.txt");
    }
}

我认为Namespace没问题。