【问题标题】:How do I add an arbitrary namespace to an XslCompiledTransform at run-time?如何在运行时将任意命名空间添加到 XslCompiledTransform?
【发布时间】:2013-06-14 10:35:06
【问题描述】:

我正在尝试对定时文本标记语言 (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 中看到的那样,转换很简单,并且基于核心数据元素 &lt;p /&gt;,因此最终命名空间变化并不重要。

由于 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 中以操作名称表(在删除上述 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 文档或其他地方找不到任何解释如何将命名空间定义动态添加到转换中的内容。

有人有什么想法吗?

【问题讨论】:

    标签: c# xml debugging xslt ttml


    【解决方案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 类型和与之关联的名称表/名称空间管理器之间的关系。

    【讨论】:

      【解决方案2】:

      我试试看,这工作。

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

      我觉得命名空间没问题。

      【讨论】:

      • 是的,基本转换适用于原始示例 XML/XSL。但是,我的观点是源 XML 可能具有不同的默认命名空间。因此,该命名空间需要从源 XML 中提取并在运行时应用于 XSL,以便它们匹配并且转换(包括需要定义默认命名空间的匹配表达式中的节点前缀)起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-02
      • 1970-01-01
      • 2013-11-02
      相关资源
      最近更新 更多