SAX 的一些乐趣

原创 2000年12月09日 02:44:00
Chris Lovett
2000年8月21日

下载 查看和下载本文的源代码

现在我知道引起大量读者评论的秘密了。只需写一篇有关读者还不能接触到的最新技术的文章就够了。现在既然提供了 .NET Framework SDK 的技术概览(英文),我希望您们都已经对其有所了解。

在本文中,我想仔细研究一下包括在 2000 年 7 月的 Microsoft XML Parser 测试版(英文)中的 Visual Basic® SAX 接口(英文)。为了作一点更改,我决定编写一些 Visual Basic 代码,而最终我却编写了一大堆代码。我还很好地实现了 MSXML2.VBSAXXMLReader30 类。应用结果如下所示:


SAX Workbench

OASIS 一致性测试的利用

我从编写 Visual Basic 测试的应用着手,以便运行 OASIS 一致性测试套件(英文)。 非 MS 链接 我所从事的另一个项目需要此测试,因此我认为我会一举两得。

此测试的使用要加载一个很大的 XML 文档,它是要运行的所有测试的索引。每个测试都像下面这样列出:

   Attribute values must start with attribute names, not "?". 

每个 TEST 元素都包含以下属性:

属性 含义
TYPE not-wf 此时可认为分析器将报告一个格式明确的错误消息

invalid 此时可认为验证分析器将报告验证错误消息,而非验证的分析器则将让这些测试通过 valid 此时可认为验证分析器和非验证的分析器都将让这些测试通过 ENTITIES none 无论测试是否要求支持加载实体 ID not-wf-sa-001 唯一的测试标识符 URI not-wf/sa/001.xml 要分析的实际 XML 测试文件的位置 SECTIONS 3.1 [41] XML 1.0 规范(英文)中相关部分的引用非 MS 链接

OasisTest.cls

进入 OasisTest 类模块的主入口点取得一个指向 xmlconf.xml 主索引文档的 URL:

Public Sub run(testurl As String)

在调用它时,我将此测试索引加载到一个 DOMDocument 对象中,选择所有的 TEST 元素,然后调用我的 SAX 测试代码,其中就有关于每个测试的信息。

Dim doc As DOMDocument
Dim node As IXMLDOMElement
Dim tests As IXMLDOMNodeList
Set doc = New DOMDocument30
doc.async = False
Set tests = doc.selectNodes("//TEST")
Set node = tests.nextNode()    
While Not node Is Nothing And Not Cancel
    RunTest(node)
    node = tests.nextNode()    
Wend

我还创建了一个空文档,它将包含一个所有测试结果的日志。在用户单击生成报表按钮时,将使用 template.xsl 样式表转换此文档,以显示最终的测试报告。

为了用 MSXML2.VBSAXXMLReader 对象实际上运行此测试,我使用了下面的方法:

Public Sub RunTest(docBase As String, element As IXMLDOMElement)

首先,此方法创建一个新的 SAX 阅读器对象,并对该对象进行配置,使其能够处理外部实体和回调我实现的 IVBSAXContentHandler、IVBSAXDTDHandler 和 IVBSAXErrorHandler 等接口。

Dim ContentHandler As ContentHandler
Set ContentHandler = New ContentHandler
Dim reader As VBSAXXMLReader30
Set reader = New VBSAXXMLReader30
reader.putFeature "http://xml.org/sax/features/external-parameter-entities", True
reader.putFeature "http://xml.org/sax/features/external-general-entities", True
Set reader.contentHandler = ContentHandler
Set reader.errorHandler = ContentHandler
Set reader.dtdHandler = ContentHandler

请注意,在同一个类(即 ContentHandler 类)上实现所有这三个处理程序接口实际上相当方便。

为了开始对测试文件的实际分析,我只需调用 parseURL 方法。

reader.parseURL (uri) 

然后检查结果,在实际输出和预期输出结果之间进行比较,等等。

ContentHandler.cls

ContentHandler 类模块实现 SAX 回调接口,代码开头如下:

Option Explicit
Implements IVBSAXContentHandler
Implements IVBSAXDTDHandler
Implements IVBSAXErrorHandler

然后 ContentHandler 实现在这些接口上定义的所有方法。这个类中的大量代码处理如何生成 XML 的规范输出,此规范输出以后可用来与期望的输出进行比较。输出包括以下内容:

  • 将所有特殊字符换码为实体:特殊字符包括 & < > " 并将换行符换码为 和 。这正是测试套件中所期望的输出文件的存储方式,因此我也必须完成此任务。

  • 为属性排序:由于属性与次序无关,且一些分析器返回缺省属性的次序与其它分析器不同,排序可保证属性的次序与期望的输出文件相匹配。要做到这一点,我使用了在 MSDN 上找到的 Visual Basic QuickSort 算法。请参阅 Sorter.cls 模块。

  • 保存符号声明:符号声明来自 IVBSAXDTDHandler 接口。需要保存它们,然后进行排序,以便对照期望的输出进行比较。符号的存储和排序在 DocType.cls 模块中完成。

  • 捕获并存储错误信息:这在 IVBSAXErrorHandler fatalError 方法的实现中完成。

XML 统计数据程序包

用 SAX 级 XML 处理能做的另一件有趣的事情是对元素和属性进行计数。我编写了另一个简单的 IVBSAXContentHandler 实现,来对元素、属性、文本节点、文本字符和名称字符的数目进行计数—并显示一个“标记率”,表明在文件中相对于实际的文本内容有多少标记。正如您从下文所看到的,hamlet.xml 文件的标记相当多。

显示 hamlet.xml 的标记率的抓屏

当您进行这种计数的时候,您一定会感觉到 SAX 级处理可以有多快。对比加载 DOMDocument 对象模型,并历经树结构来计算这一切,它当然要快得多。

Filter.cls

此类模块通过根据所调用的方法对一组计数器加 1,来实现 IVBSAXContentHandler 和 IVBSAXErrorHandler SAX 回调接口。例如,startElement 方法就完成以下任务:

Private Sub IVBSAXContentHandler_startElement(
                       ByVal strNamespaceURI As String, 
                       ByVal strLocalName As String, 
                       ByVal strQName As String, 
                       ByVal Attributes As IVBSAXAttributes)
    Dim i As Integer

    Elements = Elements + 1
    NameChars = NameChars + Len(strQName)
    AttributeNodes = AttributeNodes + Attributes.length
    For i = 0 To Attributes.length - 1
        NameChars = NameChars + Len(Attributes.getQName(i))
        TextChars = TextChars + Len(Attributes.getValue(i))
    Next
End Sub
 

XML 过滤

在我为过滤器 IVBSAXContentHandler 的实现编写代码的同时,我发现添加过滤操作根本不会增加多少工作量。过滤器选项卡包含以下选项:


SAX Workbench 上的“过滤器”选项卡的抓屏

在这里我已经选择了加载 hamlet.xml 的选项,并将所有的元素名和属性名都转换为正确的大小写;例如, 将变成 等等。

格式化

SAX 级处理还使您能够通过添加新行和根据嵌套级别添加缩进,来格式化 XML 文档。您可以控制缩进量,并控制在缩进时是使用空格字符还是使用制表字符。当格式化被设置为“缩进”时,以下输入:

ChrisLovett

将变成:

ChrisLovett

缩进算法的工作原理是:保持一个整数堆栈,这些整数代表文档每一级的“内容”模型。可能的值有:

Const CONTENT_EMPTY = 0
Const CONTENT_MIXED = 1
Const CONTENT_ELEMENT = 2

新元素的内容模型开始时被当作 CONTENT_EMPTY。当调用 IVBSAXContentHandler_characters 方法时,当前元素的内容模型被设置为 CONTENT_MIXED。如果在启动一个子元素时内容并没有混合,则内容将变成 CONTENT_ELEMENT。

我还没有完整地测试过此代码,因此我建议不要将它用于行业中的大负荷应用程序。然而,在大部分时间内它看起来工作得相当不错。您还可以在它上面加上许多东西。例如,您将注意到空元素 被输出为 。这只是因为我太懒了,以致于直到出现 endelement 事件才书写 ">" 字符。这使代码有点凌乱,因为您必须牢记在书写任何文本内容或子元素之前都要写出 ">" 字符。

元素的属性

最后,这个小复选框导致所有的属性都被书写为子元素。例如,当选中此复选框时,下面的 XML:

address='67 Seventh Av.' city='Salt Lake City' state='UT' zip='84152' contract='True' '/>

将变成(在缩进也起作用时):

998-72-3567RingerAlbert801 826-0752
67 Seventh Av.
Salt Lake CityUT84152True

这是由下面的代码简单地完成的:

If (AttrsToElements) Then
    For i = 0 To Attributes.length - 1
        Name = Attributes.getQName(i)
        If (Name <> "xmlns" And Mid(Name, 1, 6) <> "xmlns:") Then
            content(Level) = CONTENT_ELEMENT 
            Call WriteIndent(Level, content(Level))
            OutputStream.Write ("<" & FilterName(Name) & ">")
            OutputStream.Write (EscContent(Attributes.getValue(i)))
            OutputStream.Write ("" & FilterName(Name) & ">")
        End If
    Next
End If

结论

包括在 2000 年 7 月的 Microsoft XML Parser 测试版(英文)中的 Visual Basic SAX 接口(英文)使编写高性能、流级别 XML 处理的应用程序变得相当容易。我花了大约一天的时间就写出了这些小样例,得到了很多乐趣。我希望您也来享受使用 SAX 的快乐。


Chris Lovett 是 Microsoft 的 XML 小组的项目经理。

归档的早期 XML 栏目

2000 年
7月25日    用 C# 编写的 ASP+ ListEditor(英文) 6月22日    从 XSL 参数获取值(英文) 5月29日    XInclude,有人吗?(英文) 4月21日    ListEditor:一个有用的 XML Web 服务(英文) 3月20日    MSXML3 性能揭密(英文) 2月21日    MSXML 性能揭密(英文) 1月17日    使用 XML 合理化您的 Web 站点(英文) 1999 年 12月15日    XML for Microsoft Windows 2000 的新增功能(英文) 9月20日    ASP 技术和 XML DOM(英文) 8月16日    架构:挖掘内部潜能(英文) 7月20日

利用Dom,Sax,Pull三种方式解析xml文件

最近找工作,看到许多公司的要求里都写了要会xml解析,所以就把之前的xml解析知识又重新回顾了一下,写个小例子. 解析xml文件常用的几种方式也就dom,sax,pull了,并且面试官经常问到的也是这...
  • qiang_xi
  • qiang_xi
  • 2015年11月29日 20:15
  • 1149

XML使用SAX解析与PULL解析的区别

XML使用SAX解析与PULL解析的区别在最近的应用中,经常会用到对XML的解析,但是一直有一个疑问就是SAX解析与PULL解析的区别到底是什么。搜索了相关的问题,整理如下,以便大家查询。我们知道,S...
  • zhliro
  • zhliro
  • 2015年07月10日 15:18
  • 1909

使用SAX方式解析XML

一、创建XML文件 目录结构: 二、新建一个带有main方法的类(SAXTest.java)public class SAXTest { public static void main(S...
  • L_in12
  • L_in12
  • 2016年07月13日 14:17
  • 4906

XML解析(一),SAX解析XML

一、概述 SAX,全称Simple API for XML,是一种以事件驱动的XMl API,是XML解析的一种新的替代方法,解析XML常用的还有DOC解析,PULL解析(Android特有),SAX...
  • ydxlt
  • ydxlt
  • 2015年12月05日 09:38
  • 15002

Java解析xml文档之SAX解析

sax解析是一种边读边解析,仅向前读取,不能修改,用来读。sax创建XMLReader三步:SAXParserFactory factory = SAXParserFactory.newInstanc...
  • new___Smile
  • new___Smile
  • 2016年07月16日 00:59
  • 3510

SAX解析与DOM解析的区别

1.SAX解析(Simple API for XML) SAX解析方式:逐行扫描文档,一遍扫描一遍解析。相比于DOM,SAX可以在解析文档的任意时刻停止解析解析,是一种速度更快,更高效的方法。 优点:...
  • sinat_27170093
  • sinat_27170093
  • 2017年01月07日 13:50
  • 885

浅析SAX,DOM,JAXP,JDOM与DOM4J之间的关系

众所周知,SAX与DOM是JAVA中两大核心XML解析API类库,今日兴起看了看相关资料,在这里总结总结。   首先登场的是SAX,SAX是Simple API for XML的简称。另外SAX只...
  • xiongqi215
  • xiongqi215
  • 2013年08月21日 23:10
  • 2346

SAX解析xml characters方法要注意的问题

前段时间,在写一段解析xml的代码时发现了一个问题。
  • zhutulang
  • zhutulang
  • 2014年07月13日 11:18
  • 7159

SAX解析XML 详解

SAX解析XML 详解
  • liuquan0071
  • liuquan0071
  • 2015年12月11日 15:28
  • 1884

android 自学日记(六) ——SAX解析中换行问题解决

今天在写一个小项目的时候用到了SAX解析,遇到了一点小问题,网上找了好久都没有解决,最后还是自己发现了解决方法,特地和大家分享一下!...
  • Hubert_bing
  • Hubert_bing
  • 2016年05月05日 20:42
  • 794
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:SAX 的一些乐趣
举报原因:
原因补充:

(最多只允许输入30个字)