65.9K
CodeProject 正在变化。 阅读更多。
Home

XML 字符串浏览器(就像 Internet Explorer)使用 WebBrowser 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (13投票s)

2008 年 3 月 12 日

CPOL

6分钟阅读

viewsIcon

133549

downloadIcon

3245

本文将向您展示如何在不使用 XML 文件的情况下,在浏览器中(类似于 Internet Explorer)以彩色可折叠树视图显示 XML 字符串/XmlDocument。

引言

本文将向您展示如何像 Internet Explorer 渲染 XML 文件一样渲染 **XML 字符串** 或 `System.Xml.XmlDocument`。表面上看,这似乎是一项简单的任务。**错了!**如果您认为这是一项简单的任务,那么您会惊讶于自己错了(我也是)。本示例中的用户控件很有用,因为许多现代技术都依赖于 XML。因此,缺少一种简单的方法来以美观的格式显示*中间*或*生成的* XML 是不令人满意的。这就是我创建此控件的原因——因为我找不到像这样的控件!是否曾想过如何在不使用 XML 文件的情况下显示格式化的 XML 字符串或 XML 文档?您是否像我一样绝望,因为 `WebBrowser` 控件没有提供简单的方法来实现这一点?如果是这样,请不要再犹豫了——本文将为您提供所需的答案。

下面的屏幕截图描绘了示例 XML 文件的渲染。在渲染**之前**,该文件已加载到 `string` 中。

XMLBrowser/xmlrender_20.jpg

背景

多年来,我一直在架构和开发解决方案,并从互联网上获益良多。我无法告诉您搜索某物节省了我多少时间。有这么多很棒的文章和很棒的作者。这就是为什么我决定写我的第一篇文章——回馈给我很多东西的社区。此外,我还花了几个小时寻找一种方法来以类似于 Internet Explorer 的方式显示 XML `string`。

我感到惊讶的是,尽管许多现代技术都依赖于 XML,但 `WebBrowser` 控件本身并不像 Internet Explorer 那样允许您查看 XML(除非您正在查看 XML 文件)。依赖 XML 文件存在来查看格式化的 XML 是完全不可取的。

与许多开发项目一样,这一个也经历了数小时(数天)的研究。

我的旅程始于发现一个 XSL 文件(称为*DEFAULTSS.XSL*),该文件使用了旧的不受支持的 XSL 命名空间“*http://www.w3.org/TR/WD-xsl*”。最初,将此现代化为 XSLT 1.0 的努力并不完全成功,因为缺乏一种简单的方法来选择和渲染 `namespace` 和 `CData` 部分。

幸运的是,我记得旧的做事方式(`FreeThreadedDOMDocument` 和老式的 `XSLTemplate`)。旧的 XSL 处理器仍然可以在 .NET 中使用。这就是我所需要的突破。在那之后,我对完成 *DEFAULTSS.XSL* 文件的 XSLT 1.0 版本(Internet Explorer 使用它来渲染 XML 文件)进行了重要(且富有挑战性)的更改。该版本最初由 Steve Muench 转换,您可以在此处找到。

自写这篇文章以来,我尝试了几种新的 XML 渲染方法。一种是使用 SAXON 解析器(*XSLTHtml20.xslt*);另一种(我认为是最好的方法)是使用命名空间轴并在将 `CData` 节点传递给 XSLT 转换之前对其进行转义(*XmlToHtml10Basic.xslt*)。

Using the Code

本文的源代码包含两个项目:`XmlRender` 和 `XmlRenderTestApp`。

名称 描述
XmlRender 此项目包含几个 `static` 类方法
  • RenderXmlToHtml.Render(XmlDocument xmlToRender,XmlRender.XmlBrowser.XslTransformType xslTransformType)
  • RenderXmlToHtml.Render(System.Xml.XmlDocument xmlToRender)
  • RenderXmlToHtml.Render(string xmlToRender)

它还包含 `XmlBrowser` 控件,该控件扩展了 `WebBrowser`,并带有三个设置:

  • System.Xml.XmlDocument XmlDocument
  • string XmlText
  • XmlRender.XmlBrowser.XslTransformType

所有这些属性都用于向浏览器指示我们希望显示格式化的 XML(以及它应该如何渲染)。

XmlRenderTestApp 此项目包含一个 Web 窗体,其中有一个下拉列表用于选择 XML 文件,单选按钮用于指示我们是否要使用 XSL 或 XSLT 转换,以及 `XmlBrowser` 控件。每个文件都会加载到一个 `string` 中,并设置 `XmlBrowser` 的 `XmlDocument` 属性。

XmlRender 代码

/// <summary />
/// This class has the methods required to render XML as pretty HTML.
/// </summary />
internal static class RenderXmlToHtml
{
    #region Render(XmlDocument xmlToRender, 
            XmlBrowser.XslTransformType xslTransformType)
    /// <summary>
    /// This method determines which output type to render
    /// </summary>
    internal static string Render(XmlDocument xmlToRender, 
             XmlBrowser.XslTransformType xslTransformType)
    {
        if (xslTransformType == XmlBrowser.XslTransformType.XSL)
            return Render(xmlToRender.OuterXml);
        else if (xslTransformType == XmlBrowser.XslTransformType.XSLT10)
            return Render(xmlToRender,XmlRender.Properties.Resources.XmlToHtml10);
        else if (xslTransformType == XmlBrowser.XslTransformType.XSLT10RegExp)
            return Render(xmlToRender,
                   XmlRender.Properties.Resources.XmlToHtml10Plus);
        return string.Empty;
    }
    #endregion

    #region Render(string xmlToRender)
    /// <summary>
    /// This method has to use the old XSL Processor because of the fact that the
    /// http://www.w3.org/TR/WD-xsl namespace is unsupported.
    ///
    /// The simplest way was to use the old xsl instead of re-inventing the wheel.
    /// I could not find a way to easily fully convert this to XSLT 1.0 due to 
    /// the lack of namespace and cdata 'selectability'.
    ///
    /// This brings back memories :)
    /// </summary>
    internal static string Render(string xmlToRender)
    {
        XSLTemplate oXSLT;
        FreeThreadedDOMDocument oStyleSheet;
        IXSLProcessor oXSLTProc;
        DOMDocument oXMLSource;

        try
        {
            oXSLT = new XSLTemplate();
            oStyleSheet = new FreeThreadedDOMDocument();
            oXMLSource = new DOMDocument();

            oStyleSheet.async = false;
            oStyleSheet.loadXML(XmlRender.Properties.Resources.XMLToHTML);

            oXSLT.stylesheet = oStyleSheet;

            oXSLTProc = oXSLT.createProcessor();

            oXMLSource.async = false;
            oXMLSource.loadXML(xmlToRender);
            oXSLTProc.input = oXMLSource;

            oXSLTProc.transform();

            return oXSLTProc.output.ToString();
        }
        catch (Exception e)
        {
            return e.Message;
        }
        finally
        {
            oXSLT = null;
            oStyleSheet = null;
            oXMLSource = null;
            oXSLTProc = null;
        }
    }
    #endregion

    #region Render(XmlDocument xmlToRender)
    /// <summary>
    /// This method uses the XsltCompiledTransform to transform XML to pretty HTML.
    /// Unfortunately, this method does require
    /// using EXSL/MSXML but it is XSLT 1.0 compliant.
    /// </summary>
    internal static string Render(XmlDocument xmlToRender,string xsltDocument)
    {
        XslCompiledTransform xslCompiledTransform;
        XmlReader xmlReader;
        System.IO.StringReader stringReader;
        StringBuilder stringBuilder;
        XmlWriter xmlWriter;
        XsltSettings xsltSettings;
        try
        {
            xslCompiledTransform = new XslCompiledTransform(true);
            stringReader = new System.IO.StringReader(xsltDocument);
            xmlReader = XmlReader.Create(stringReader);
            xsltSettings = new XsltSettings(true, true);
            xslCompiledTransform.Load(xmlReader,xsltSettings,new XmlUrlResolver());

            stringBuilder = new StringBuilder();
            xmlWriter = XmlWriter.Create(stringBuilder);
            XsltArgumentList a = new XsltArgumentList();
            // Need to pass the xml string as an input parameter so
            // we can do some parsing for extra bits that XSLT won't do.
            a.AddParam("xmlinput", string.Empty, xmlToRender.OuterXml);
            xslCompiledTransform.Transform(xmlToRender, a, xmlWriter);
            return stringBuilder.ToString();
        }
        catch (Exception e) { return e.Message; }
        finally
        {
            xslCompiledTransform = null;
            xmlReader = null;
            stringReader = null;
            stringBuilder = null;
            xmlWriter = null;
            xsltSettings = null;
        }
    }
    #endregion
}

XmlRenderTestApp 代码

public partial class Form1 : Form
{
    // used to store location of xml files
    string _xmlDirectory;

    #region Constructor
    /// <summary>
    /// Constructor
    /// </summary>
    public Form1()
    {
        InitializeComponent();
        // Set up xmlDirectory and load combo box. 
        //
        // when you build the project all files are copied to $(TargetDir)\XML Files
        // just add any additional files you want to the XML files directory of this project
        FileInfo fi = new FileInfo(Assembly.GetExecutingAssembly().FullName);
        _xmlDirectory = fi.DirectoryName + @"\XML Files\";
        foreach(FileInfo file in new DirectoryInfo(_xmlDirectory).GetFiles("*.*"))
        {
            comboBox1.Items.Add(file.Name);
        }
    }
    #endregion

    #region comboBox select
    /// <summary>
    /// Xml file to load into a string for rendering.
    /// </summary>
    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
        XmlDocument _xd;
        try
        {
            _xd = new XmlDocument();
            string fileName = _xmlDirectory + comboBox1.SelectedItem.ToString();
            System.Uri _uri = new Uri(fileName);

            _xd.Load(fileName);

            // the following lines are of particular interest. 
            // I'm rendering an XML string to HTML (using good old XSL).
            // This gives us the flexibility to build XML and 
            // present it in the browser in a nice way.
            //
            // I have to do this because we can't send an XML string to the browser 
            // and expect it to render like IE does

            // Send an XmlDocument to the XmlBrowser
            xmlBrowser1.XmlDocument = _xd;

            // Uncomment the following to send Xml Text to the XmlBrowser
            //xmlBrowser1.XmlText = _xd.OuterXml;

            // Uncomment the following to see how
            // the webbrowser control renders XML text
            //xmlBrowser1.DocumentText = _xd.OuterXml;

            // Uncomment the following to compare
            // to WebBrowser native rendering of Xml Documents
            //xmlBrowser1.Url = _uri;
        }
        finally
        {
            _xd = null;
        }
    }
    #endregion

    #region RadioButtons
    private void radioButton1_CheckedChanged(object sender, EventArgs e)
    {
        xmlBrowser1.XmlDocumentTransformType = 
           XmlRender.XmlBrowser.XslTransformType.XSL;
    }

    private void radioButton2_CheckedChanged(object sender, EventArgs e)
    {
        xmlBrowser1.XmlDocumentTransformType = 
           XmlRender.XmlBrowser.XslTransformType.XSLT10;
    }
    private void radioButton3_CheckedChanged(object sender, EventArgs e)
    {
        xmlBrowser1.XmlDocumentTransformType = 
           XmlRender.XmlBrowser.XslTransformType.XSLT10RegExp;
    }
    #endregion

}

XmlBrowser 代码

// Use this control if you want a webBrowser which has been extended 
// to allow for Xml Rendering
public partial class XmlBrowser : WebBrowser
{
    #region Types/Private Variables
    /// <summary>
    /// Type to determine which XSL/XSLT to use in transformation
    /// </summary>
    public enum XslTransformType
    {
        XSL,
        XSLT10,
        XSLT10RegExp
    }
    // XmlDocument to store Xml data for rendering
    private XmlDocument _xmlDocument;
    // XslDocument to use when rendering Xml
    private XslTransformType _xslTransformType;
    #endregion

    #region Constructor
    public XmlBrowser()
    {
        InitializeComponent();
    }
    #endregion

    #region Properties

    #region XmlText
    /// <summary>
    /// Property to set Xml Text for webbrowser
    /// </summary>
    [Category("XmlData")]
    [Description("Use this property to set the XmlText 
                  for rendering in the webbrowser.")]
    public string XmlText
    {
        set
        {
            if (value != string.Empty)
            {
                _xmlDocument = new XmlDocument();
                _xmlDocument.LoadXml(value);
                this.DocumentText = 
                  RenderXmlToHtml.Render(_xmlDocument,_xslTransformType);
            }
        }
        get
        {
            if (_xmlDocument == null)
                return string.Empty;
            else
                return _xmlDocument.OuterXml;
        }
    }
    #endregion

    #region XmlDocument
    /// <summary>
    /// Property to set XmlDocument for webbrowser
    /// </summary>
    [Category("XmlData")]
    [Description("Use this property in your code to set the 
          System.Xml.XmlDocument for rendering in the webbrowser.")]
    public XmlDocument XmlDocument
    {
        get
        {
            return _xmlDocument;
        }
        set
        {
            if (value != null)
            {
                _xmlDocument = value;
                this.DocumentText = 
                  RenderXmlToHtml.Render(_xmlDocument,_xslTransformType);
            }
        }
    }
    #endregion

    #region XmlDocumentTransformType
    /// <summary>
    /// Change this to determine which transform type to use
    /// </summary>
    [Category("XmlData")]
    [Description("Use this property to specify to use either 
       the XSL or XSLT 1.0 compliant stylesheet for rendering.")] 
    public XslTransformType XmlDocumentTransformType
    {
        get
        {
            return _xslTransformType;
        }
        set
        {
            _xslTransformType = value;
        }
    }
    #endregion

    #endregion
}

关注点

我曾考虑在此列出 *XmlToHtml10.xslt*/*XmlToHtml10Plus.xslt* 文件,但它们很大。但我会告诉您其中的亮点:

  • *XmlToHtml10.xslt* 在 JavaScript 中使用了一些 `MSXML2.FreeThreadedDOMDocument`(我最初曾考虑使用正则表达式,但一开始没有这样做,因为我不想花太多时间来写这篇文章)。
  • *XmlToHtmlPlus.xslt* 只使用正则表达式。它**不**使用 `MSXML2.FreeThreadedDOMDocument`。这尤其好,因为我认为它可能更有效。
  • XSLT 中有一些巧妙的 XPath 构建,用于将当前上下文节点标识给 JavaScript 函数。(请参阅关于 `CDATA` 处理的部分。)
  • 有关 XSLT 递归的有趣示例,请查看 `xmlnsSelector` 模板或 `getXPath` 模板。XSLT 递归总是很有趣的。
  • 最后但并非最不重要的,如果您在 VS2005 中调试/显示 *XmlToHtml10.xslt*/*XmlToHtml10Plus.xslt* 的输出,它将无法利用我添加的有趣部分。您只能在运行时看到这一点。

我实际上正在写另一篇文章,演示如何通过在运行时向通用 `DataSet` 提供 XSD,轻松地用从任何数据库检索到的表数据来塑造 `DataSet`。缺乏 XML 可视性导致我分心并先写了这篇文章!您可以此处找到我提到的文章。

希望您喜欢这篇文章。我真的很享受思考它带来的挑战。

以下是此项目中包含的转换文件的简要概述:

名称 XSLT 处理器 使用扩展 描述
XMLToHTML.xsl XSL 原始 Microsoft XSL
XMLToHTML10.xslt XSLT1.0 使用 `FreeThreadDomDocument`/正则表达式和 XSLT 来访问命名空间节点和 `CDATA`
XMLToHTML10Plus.xslt XSLT1.0 仅使用正则表达式来访问 `CDATA` 和命名空间节点
XMLToHTML10Cdata.xslt XSLT1.0 使用正则表达式来访问 `CDATA`,并使用命名空间轴来访问命名空间
XMLToHTML10Basic.xslt XSLT1.0 使用 `CDATA` 的转义和命名空间轴
XMLToHtml20.xslt XSLT2.0 不渲染命名空间或 `CDATA`

一个普遍的观点是:不使用 JavaScript 扩展的转换比使用 JavaScript 扩展的转换性能更好。

历史

  • 2008 年 3 月 12 日:首次发布
  • 2008 年 3 月 13 日:修复了渲染问题,现在 XML `string` 的显示与 Internet Explorer 查看 XML 文档完全相同。添加了 `XMLBrowser` 控件,具有两个属性:`XmlDocument` 和 `XmlText`。这是为了将 XML 格式化封装到 `XmlRender` 类库中可重用的用户控件。
  • 2008 年 3 月 17 日:添加了 *DEFAULTSS.XSL* 的 XSLT 等效版本。另外,在测试应用程序中添加了一些复选框,允许在 XSL/XSLT 转换之间切换。
  • 2008 年 3 月 18 日:修改窗体以自动填充 *$(TargetDir)\XML Files* 目录中的文件。文件在构建项目时(通过构建事件)自动复制到此目录。无需为每个文件设置构建选项(只需将其复制到 `XmlRenderTestApp` 项目中的 XML 文件目录)。
  • 2008 年 3 月 24 日:添加了 XSLT 文件的纯正则表达式版本(称为 *XmlToHtml10Plus.xslt*)。另外,对 *XmlToHtml10.xslt* 进行了轻微的更正,以保持一致性。
  • 2008 年 3 月 26 日:对 *XmlToHtml10Plus.xslt* 和 *XmlToHtml10.xslt* 中与 `xmlns` 类属性相关的部分进行了小修复。
  • 2008 年 4 月 4 日:添加了 SAXON 实现的 XML 渲染——仍在进行中,但具有非常酷的功能。
  • 2008 年 4 月 7 日:使用 Microsoft XSLT 解析器的大型 XML 选项不会添加命名空间和 `CDATA` 节点。速度非常快。
  • 2008 年 4 月 14 日:使用 Microsoft XSLT 解析器的大型 XML 选项将添加命名空间和 `CDATA` 节点。速度非常快。使用命名空间轴和 `CDATA` 的转义,以便可以在 XSLT 中使用它。

参考文献

© . All rights reserved.