C# - 是不是可以(以及如何)使用 SgmlReader 执行 XSL 转换
Posted
技术标签:
【中文标题】C# - 是不是可以(以及如何)使用 SgmlReader 执行 XSL 转换【英文标题】:C# - Is it possible (and how) to perform XSL transformations using SgmlReaderC# - 是否可以(以及如何)使用 SgmlReader 执行 XSL 转换 【发布时间】:2011-05-17 22:38:22 【问题描述】:我需要使用 XSLT 转换 html 网页的内容 .因此我使用SgmlReader 并编写了如下所示的sn-p(我 想了想,最后,它也是一个 XmlReader ...)
XmlReader xslr = XmlReader.Create(new StringReader(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">" +
"<xsl:output method=\"xml\" encoding=\"UTF-8\" version=\"1.0\" />" +
"<xsl:template match=\"/\">" +
"<XXX xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><xsl:value-of select=\"count(//br)\" /></XXX>" +
"</xsl:template>" +
"</xsl:stylesheet>"));
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(xslr);
using (SgmlReader html = new SgmlReader())
StringBuilder sb = new StringBuilder();
using (TextWriter sw = new StringWriter(sb))
using (XmlWriter xw = new XmlTextWriter(sw))
html.InputStream = new StringReader(Resources.html_orig);
html.DocType = "HTML";
try
xslt.Transform(html, xw);
string output = sb.ToString();
System.Console.WriteLine(output);
catch (Exception exc)
System.Console.WriteLine("0 : 1", exc.GetType().Name, exc.Message);
System.Console.WriteLine(exc.StackTrace);
不过,我收到了错误消息
NullReferenceException : Object reference not set to an instance of an object.
at MS.Internal.Xml.Cache.XPathDocumentBuilder.Initialize(XPathDocument doc, IXmlLineInfo lineInfo, String baseUri, LoadFlags flags)
at MS.Internal.Xml.Cache.XPathDocumentBuilder..ctor(XPathDocument doc, IXmlLineInfo lineInfo, String baseUri, LoadFlags flags)
at System.Xml.XPath.XPathDocument.LoadFromReader(XmlReader reader, XmlSpace space)
at System.Xml.XPath.XPathDocument..ctor(XmlReader reader, XmlSpace space)
at System.Xml.Xsl.Runtime.XmlQueryContext.ConstructDocument(Object dataSource, String uriRelative, Uri uriResolved)
at System.Xml.Xsl.Runtime.XmlQueryContext..ctor(XmlQueryRuntime runtime, Object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, WhitespaceRuleLookup wsRules)
at System.Xml.Xsl.Runtime.XmlQueryRuntime..ctor(XmlQueryStaticData data, Object defaultDataSource, XmlResolver dataSources, XsltArgumentList argList, XmlSequenceWriter seqWrt)
at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlSequenceWriter results)
at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer, Boolean closeWriter)
at System.Xml.Xsl.XmlILCommand.Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results)
at System.Xml.Xsl.XslCompiledTransform.Transform(XmlReader input, XmlWriter results)
我找到了一种解决方法,将 HTML 转换为 XML 然后应用转换,但这是一个低效的解决方案,因为:
-
中间 XHTML 输出进入缓冲区,因此需要额外的内存
转换过程需要额外的CPU处理
并且相同的层次结构被遍历了两次(理论上是不必要的)。
所以(因为我知道 *** 社区总是提供很好的答案,而其他 C# 论坛让我完全失望;o) 我会寻找反馈和建议,以便直接使用 HTML 执行 XSL 转换(即使 SgmlReader 需要被另一个类似的库替换)。
【问题讨论】:
关于下划线问题:XSLT 1.0 使用 XML 输入树(XSLT 2.0 可以使用未解析的资源)。如果您有一些不是 XML 树的东西,则需要使用某种方法将其映射到 XML 树。 Olemis,请注意,XslCompiledTransform 是一个 XSLT 1.0 处理器,因此如果您在样式表中使用 version="2.0",它会在向前兼容模式下运行,并且您不会报告所有 XSLT 1.0 语法错误。因此,我将开始在您的样式表中设置 version="1.0",因为 XslCompiledTransform 将在 Load 调用中通知您您的样式表在语法上不正确,因为不允许在 xsl:template 中使用 xsl:output。我不确定这是否有助于解决您在提供 SgmlReader 时遇到的问题,您需要提供一个您正在使用的示例 HTML,它会给出异常。 【参考方案1】:即使SgmlReader
类扩展了XmlReader
类,也不意味着它的行为也像XmlReader
一样。
从技术上讲,SgmlReader
是 XmlReader
的子类也没有任何意义,因为 SGML 是 XML 的超集而不是子集。
您没有写下转换的目的,但总的来说 HTML Agility Pack 是处理 HTML 的好选择。
【讨论】:
恕我直言,这 真的 是有道理的(至少从 OOP 的角度来看),因为 XmlReader是一种类型,任何实现这个接口的东西都可以被操纵(在这种情况下被读取)就好像它是一个XML文档。事实上,为 YAML 、 INI 文件等其他结构化格式实现 XmlReader 是有意义的,即使它们根本不是标记,它们是您可能希望以结构化方式阅读和转换的结构化文档。只是我的看法。 @Olemis Lang:在我看来这没有意义,因为 XmlReader 需要格式良好的文档,即具有树结构的文档。 SGML 没有提供这一点,因此ReadSubtree
或ReadInnerXml
之类的方法没有任何意义。因此,在 SgmlReader 上运行 XSLT 的情况下,您实际上可能会遇到底层引擎调用这些方法之一但没有得到预期结果的情况。另请参阅 Alejandro 对 XSLT 的预期的评论。
& @Alejandro :我认为这就是 SgmlReader 的用途(即处理格式错误的 HTML,就像它的 XHTML 等价物一样,所有这些在 HTML 的特定情况下)。实际上,如果您查看回溯,似乎阅读器用于在内部构建 System.Xml.XPath.XPathDocument 的实例,这是编译后的 XSL 使用的实例i> 在引擎盖下进行转换。无论如何,我会在一段时间内尝试这些方法,以确认 ReadSubtree 等会发生什么。谢谢【参考方案2】:
您是否尝试过使用HTML Agility Pack 而不是SgmlReader
?您可以将 html 加载到其中,并直接对其运行转换。不过,如果 XML 文档是在内部创建的,我并不肯定——尽管看起来好像不是,但您可能希望将内存和 CPU 使用率与您尝试和丢弃的转换方法进行比较。
//You already have your xslt loaded into var xslt...
HtmlDocument doc = new HtmlDocument();
doc.Load( ... ); //load your HTML doc, or use LoadXML from a string, etc
xslt.Transform(doc, xw);
另请参阅此问题:How to use HTML Agility pack
【讨论】:
感谢 Philip 的回复,但构建 HTML DOM 可能会很耗时,而且还会使用额外的内存。我真的很想避免将对象加载到内存和额外的处理,因为应用程序应该在功能有限的设备上运行。这就是为什么我一直在寻找一种将 HTML XMLReader 直接提供给 XSLT 的方法(但是,考虑到回溯,它似乎构建了一个 System.Xml.XPath.XPathDocument 内部,所以也许我可以想象的任何优化都只是浪费时间......)以上是关于C# - 是不是可以(以及如何)使用 SgmlReader 执行 XSL 转换的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Unity Engine 确定设备是手机还是平板电脑,是不是可以在 C# 中完成?