1

我正在尝试对定时文本标记语言 (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 />,因此最终命名空间变化并不重要。

由于 XSLT 文件具有明确定义的名称空间前缀 ( tt),因此上述 XSL 当前适用于某些源文件。但是,当遇到另一个具有不同默认命名空间的源文件时,这不起作用。

所以我需要找到一种方法,为 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中以操作名称表(在删除xmlns:tt="..."上述 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);

这一直运行到实际的转换,在那里我得到一个XslLoadException说明Prefix 'tt' is not defined.

我在这一点上不知所措。我可以从搜索中找到的所有内容都讨论了将名称空间放入 XSLT 文档(我已经拥有,它适用于一个名称空间值)。我在 MSDN 文档或其他地方找不到任何解释如何将命名空间定义动态添加到转换中的内容。

有人有想法么?

4

2 回答 2

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

这行得通,但对我来说有点hacky。我认为核心问题是我不了解 ( Xml| XPath)Document类型与与它们关联的名称表/命名空间管理器之间的关系。

于 2011-10-26T15:05:27.497 回答
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");
    }
}

我认为命名空间没有问题。

于 2011-10-25T22:41:59.483 回答