实用 XML (pXML) 的开源解析器





5.00/5 (5投票s)
介绍用于“实用 XML” (pXML) 的开源核心库(读取器和写入器)。
目录
引言
我之前的文章《对更好的 XML/HTML 语法的建议》提出了一个名为 practicalXML (pXML) 的新 XML/HTML 语法。pXML 比 XML 更简洁,并具有其他优点。
在本文中,我将介绍一个 pXML 解析器。该解析器是用 Java 编写的,根据 MIT 协议开源,源代码可在 Github 上找到。本文使用的示例也在 Github 上。
有关 pXML 的更多信息可以在其 网站上找到。
pXML 语法刷新
如果您从未听说过 pXML,您可能想先阅读《对更好的 XML/HTML 语法的建议》。那篇文章介绍了 pXML 并解释了其原理。
以下是《语法比较》章节的摘录
空元素
XML: <br />
pXML: [br]
带有文本内容的元素
XML: <summary>text</summary>
pXML: [summary text]
带有子元素的元素
XML: <ul>
<li>
<div>A <i>friendly</i> dog</div>
</li>
</ul>
pXML: [ul
[li
[div A [i friendly] dog]
]
]
属性
XML: <div id="unplug_warning" class="warning big-text">Unplug power cord before opening!</div>
pXML: [div (id=unplug_warning class="warning big-text")Unplug power cord before opening!]
转义
XML: <note>Watch out for <, >, ", ', &, [, ], and \ characters</note>
pXML: [note Watch out for <, >, ", ', &, \[, \], and \\ characters]
注释
Single comment:
XML: <!-- text -->
pXML: [- text -]
Nested comments:
XML: not supported
pXML: [- text [- nested -] -]
使用示例
在解释解析器是如何实现的之前,让我们先通过一些高级用法示例来看看解析器能做什么。目的是展示 XML 技术如何与 pXML 一起使用。
从 pXML 到 XML 再回来
为了体现 pXML 中的“p
”(“p
”代表 practical),我们显然需要能够将 pXML 转换为 XML,以及将 XML 转换为 pXML。本章将展示如何执行此操作的示例。
Hello World
从 PXML 到 XML
下面的代码演示了最简单的 pXML 文档 - 一个名为 hello
的空根元素
[hello]
要将 pXML 转换为 XML,在 dev.pxml.core.utilities
包中有一个名为 PXMLToXMLConverter
的实用类。该类包含名为 pXMLFileToXMLFile
的方法,其签名如下
public static void pXMLFileToXMLFile _
( @NotNull File pXMLFile, @NotNull File XMLFile ) throws Exception
该方法是重载的。输入参数可以是 File
类型(如上所示)、Path
或 String
类型。
假设上面的 pXML [hello]
代码存储在文件 hello.pxml 中。以下指令将 hello.pxml 转换为 hello.xml
pXMLFileToXMLFile ( "hello.pxml", "hello.xml" );
不出所料,结果文件 hello.xml 包含以下代码
<?xml version="1.0" encoding="UTF-8"?>
<hello />
包含本文所有源代码示例的完整测试套件可在 Github 仓库中找到。该仓库使用 Gradle 构建工具。
解析器的 Java API 文档可在 pXML 网站上找到。
如果您想在自己的环境中尝试上述示例,可以按以下步骤操作
- 如果尚未完成,请安装 Java 11 或更高版本。
- 使用您选择的工具(例如 Gradle、IntellijIdea、Eclipse)或仅使用原生 Java 创建一个 Java 应用程序。
- 访问 pXML 的 下载页面,下载最新的 .jar 文件,并将其作为依赖项添加到您的 Java 项目中。
- 修改主类,使其包含以下代码
package tests.pxml.hello; import static dev.pxml.core.utilities.PXMLToXMLConverter.*; public static void main ( String[] args ) { try { pXMLFileToXMLFile ( "input/hello.pxml", "output/hello.xml" ); } catch ( Exception e ) { e.printStackTrace(); } }
注意
根据需要修改 tests.pxml.hello 以及两个文件的路径。接受绝对和相对文件路径。相对路径相对于您的工作目录。
- 创建文件 input/hello.pxml,内容为
[hello]
。 - 创建目录 output。
- 执行应用程序。
- 在编辑器中打开生成的文件 output/hello.xml 以验证其内容。
从 XML 到 PXML
从 XML 转换为 pXML 也很容易。这可以通过类 dev.pxml.core.utilities.XMLToPXMLConverter
中的方法 XMLFileToPXMLFile
来完成。因此,以下两条 Java 语句是将 XML 文件转换为 pXML 文件所需的
import static dev.pxml.core.utilities.XMLToPXMLConverter.*;
XMLFileToPXMLFile ( "input/hello.xml", "output/hello.pxml" );
执行此代码会将文件 input/hello.xml 及其内容
<?xml version="1.0" encoding="UTF-8"?>
<hello />
...转换为 output/hello.pxml 及其以下 pXML 代码
[hello]
任何读取器/写入器
如前所述,方法 pXMLFileToXMLFile
和 XMLFileToPXMLFile
接受文件路径作为输入/输出参数。如果我们想从 URL、string
等其他源读取/写入 XML/pXML 文档,我们可以
-
使用
PXMLToXMLConverter.pipePXMLReaderToXMLWriter
从 任何 pXML 源(URL
、File
、String
等)读取,并写入 任何 XML 目标(URL
、File
、String
等)。例如,我们可以从 URL 读取 pXML 代码,并将生成的 XML 代码存储为string
。这是可能的,因为
pipePXMLReaderToXMLWriter
使用标准的java.io.Reader
来读取 pXML,并使用java.io.Writer
来写入 XML。 -
同样,
XMLToPXMLConverter.pipeXMLReaderToPXMLWriter
可用于从 任何 XML 源读取,并将结果写入 任何 pXML 目标。
登录表单
让我们创建一个更有用的示例,展示一些常用的 XML 功能。
我们将把 pXML 代码转换为 XML,然后再将生成的 XML 转换回 pXML。如果一切正常,最终的 pXML 代码必须与初始代码相同。
从 PXML 到 XML
这是一个使用嵌套元素、属性、注释和命名空间的 pXML 文档
[form
[title Login]
[note Characters \[, \], < and > are not allowed]
[fields
[- Two text fields: user and password -]
[text_entry (id=user) User]
[text_entry (id=password) Password]
]
[buttons
[button (type=submit) Ok]
[button (type=cancel color="light red") Cancel]
]
[ch:checks (xmlns:ch="http://www.example.com")
[ch:check user.size >= 2]
[ch:check password.size >= 8]
]
]
文件 input/login_form.pxml
如前所述,我们可以使用以下方法将此文件转换为 output/login_form.xml
pXMLFileToXMLFile ( "input/login_form.pxml", "output/login_form.xml" );
执行上述语句后,output/login_form.xml 的内容是
<?xml version="1.0" encoding="UTF-8"?>
<form>
<title>Login</title>
<note>Characters [, ], < and > are not allowed</note>
<fields>
<!-- Two text fields: user and password -->
<text_entry id="user">User</text_entry>
<text_entry id="password">Password</text_entry>
</fields>
<buttons>
<button type="submit">Ok</button>
<button type="cancel" color="light red">Cancel</button>
</buttons>
<ch:checks xmlns:ch="http://www.example.com">
<ch:check>user.size >= 2</ch:check>
<ch:check>password.size >= 8</ch:check>
</ch:checks>
</form>
文件 output/login_form.xml
可以观察到以下语法差异
-
pXML: [title Login] XML: <title>Login</title>
这说明了 pXML 和 XML 之间最重要的区别,正如在《对更好的 XML/HTML 语法的建议》中所解释的
-
pXML: [note Characters \[, \], < and > are not allowed] XML: <note>Characters [, ], < and > are not allowed</note>
在这里,我们可以看到在转换过程中如何应用两种方言的转义规则。pXML 使用
\
作为转义字符(与大多数编程语言一样),而 XML 使用实体。 -
pXML: [- Two text fields: user and password -] XML: <!-- Two text fields: user and password -->
转换注释的示例。
-
pXML: [text_entry (id=user) User] XML: <text_entry id="user">User</text_entry>
转换属性的示例。
注意 pXML 代码中
)
后的空格,而生成的 XML 中没有。pXML 解析器允许在)
后有一个可选的空格,该空格会被忽略。这使得可以写[text_entry (id=user) User]
而不是
[text_entry (id=user)User]
... 这可读性稍差(但仍然是有效的 pXML 代码)。
写入
[text_entry(id=user)User]
... 也会被正确解析。
-
pXML: [ch:checks (xmlns:ch="http://www.example.com") [ch:check user.size >= 2] XML: <ch:checks xmlns:ch="http://www.example.com"> <ch:check>user.size >= 2</ch:check>
pXML 解析器支持 XML 命名空间。
从 XML 到 PXML
将结果文件 output/login_form.xml 复制到 input/login_form.xml 后,我们可以使用以下方法从 XML 转换回 pXML
XMLFileToPXMLFile ( "input/login_form.xml", "output/login_form.pxml" );
以下是 output/login_form.pxml 的内容
[form
[title Login]
[note Characters \[, \], < and > are not allowed]
[fields
[- Two text fields: user and password -]
[text_entry (id="user") User]
[text_entry (id="password") Password]
]
[buttons
[button (type="submit") Ok]
[button (color="light red" type="cancel") Cancel]
]
[ch:checks (xmlns:ch="http://www.example.com")
[ch:check user.size >= 2]
[ch:check password.size >= 8]
]
]
文件 output/login_form.pxml。
如我们所见,其内容与我们最初的文件 input/login_form.pxml 的内容相同。
但是,有一个小的 语法 差异 - 这个差异不会改变文件中存储的 数据。在新文件中,引号始终用于包围属性值,即使可以省略它们(例如 id="user"
而不是 id=user
)。原因是,默认情况下,此示例中使用的 pXML 写入器始终使用引号包围属性值。它不检查值是否可以不带引号写入,因为这会降低性能。在写入器的未来版本中,可以添加一个参数来指示写入器在可能的情况下省略引号。
与 pXML 一起使用的 XML 技术
pXML 解析器最强大的功能是它能够将 pXML 文档读取到标准的 org.w3c.dom.Document
Java 对象中。
由于我们有内存中的 Java Document
对象,我们可以使用 Java 原生支持或第三方库和框架提供的所有 XML 扩展。例如,我们可以
- 使用 XML Schema(W3C)、RELAX NG 或 Schematron 验证文档
- 以编程方式遍历文档并提取数据
- 插入、修改和删除元素和属性,并将结果保存为新的 XML 或 pXML 文档
- 使用 XQuery/XPath 查询文档(搜索值、计算聚合值等)
- 使用 XSL 转换器转换文档(例如,创建不同结构的 XML 或 pXML 文档,创建纯文本文档等)
我们无法在一篇文章中涵盖所有内容,所以让我们来看看 一些 示例,了解它是如何工作的。
加载/保存“文档”
在 pXML 中使用 XML 技术,关键在于 PXMLToXMLConverter
类中的方法 pXMLToXMLDocument
。此方法从任何源(文件、URL、字符串等)读取 pXML 文档,并将其加载到标准的 Java org.w3c.dom.Document
对象中。该方法的签名是
public static Document pXMLToXMLDocument (
@NotNull Reader pXMLReader, Object pXMLResource ) throws Exception
如所示,该方法使用 Java Reader
来读取 pXML 代码,并返回一个 Document
对象。输入参数 pXMLResource
只是一个可选参数,用于在错误消息中包含资源的名称(例如“文件 foo/bar.pxml 中的错误
”)。
如果发生任何错误,将抛出异常。
数据加载后,我们可以对文档执行所有操作:验证、查询、修改、转换等。
与方法 pXMLToXMLDocument
对应的是 XMLToPXMLConverter
类中的 XMLDocumentToPXML
。该方法定义为
public static void XMLDocumentToPXML (
@NotNull Document XMLDocument, @NotNull Writer pXMLWriter ) throws Exception
该方法读取标准的 Java Document
对象,并将 pXML 数据写入任何 Java Writer
(例如 FileWriter
、StringWriter
等)。
验证
验证 XML 数据的一种常见方法是使用 XML Schema。XML Schema 本身就是一个 XML 文档,其中包含 XML 数据文档必须遵守的规则。
这是一个定义书籍列表的简单 XML 文档示例
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<isbn>978-0135957059</isbn>
<title>The Pragmatic Programmer: Your Journey to Mastery</title>
<price>41.41</price>
</book>
<book>
<isbn>978-0735619678</isbn>
<title>Code Complete: A Practical Handbook of Software Construction</title>
<price>45.32</price>
</book>
<book>
<isbn>978-0134685991</isbn>
<title>Effective Java</title>
<price>44.10</price>
</book>
</books>
文件 input/books.xml
使用 pXML 定义的相同数据如下所示
[books
[book
[isbn 978-0135957059]
[title The Pragmatic Programmer: Your Journey to Mastery]
[price 41.41]
]
[book
[isbn 978-0735619678]
[title Code Complete: A Practical Handbook of Software Construction]
[price 45.32]
]
[book
[isbn 978-0134685991]
[title Effective Java]
[price 44.10]
]
]
文件 input/books.pxml
上述 XML 可以使用此 XML Schema 进行验证
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="books">
<xs:complexType>
<xs:sequence>
<xs:element name="book" type="booktype" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="booktype">
<xs:sequence>
<xs:element name="isbn" type="xs:string"/>
<xs:element name="title" type="xs:string"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
文件 input/books.xsd
由于 XML Schema 本身就是一个标准的 XML 文档,我们也可以使用 pXML 来定义它,如下所示
[xs:schema (xmlns:xs=http://www.w3.org/2001/XMLSchema)
[xs:element (name=books)
[xs:complexType
[xs:sequence
[xs:element (name=book type=booktype minOccurs=1 maxOccurs=unbounded)]
]
]
]
[xs:complexType (name=booktype)
[xs:sequence
[xs:element (name=isbn type=xs:string)]
[xs:element (name=title type=xs:string)]
[xs:element (name=price type=xs:decimal)]
]
]
]
文件 input/books.pxsd
因此,有四种可能的组合可以验证数据
数据格式 | Schema 格式 |
---|---|
XML | XML |
XML | pXML |
pXML | XML |
pXML | pXML |
每种组合的示例都包含在 示例仓库中。
类 dev.pxml.core.utilities.XMLSchemaValidator
提供了 static
方法来验证数据。例如,使用 pXML Schema 文档验证 pXML 数据(例如,使用 books.pxsd 验证 books.pxml)可以通过以下单行代码完成
XMLSchemaValidator.validatePXMLFileWithPXMLSchemaFile (
new File ( "input/books.pxml" ),
new File ( "input/books.pxsd" ) );
如果数据无效,将抛出异常。例如,如果一本书使用标签 ibn
而不是 isbn
,则会报告以下错误
Invalid content was found starting with element 'ibn'. One of '{isbn}' is expected.
转换
XML 转换是另一个非常有用的 XML 功能。它用于将一个 XML 文档转换为另一个文档。输出文档可以是另一个 XML 文档、HTML 文档或任何其他纯文本文档。转换过程由 转换语言 描述。最流行的转换语言是 XSLT,它被定义为一个 XML 文档。
例如,让我们重用上一个“验证”示例中的书籍数据。
[books
[book
[isbn 978-0135957059]
[title The Pragmatic Programmer: Your Journey to Mastery]
[price 41.41]
]
[book
[isbn 978-0735619678]
[title Code Complete: A Practical Handbook of Software Construction]
[price 45.32]
]
[book
[isbn 978-0134685991]
[title Effective Java]
[price 44.10]
]
]
文件 input/books.pxml
现在我们想创建一个 HTML 文档来显示书籍的表格。我们可以使用以下用 XML 编写的 XSLT 文档
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="/">
<html>
<head>
<title>Books</title>
<style>
table, th, td {
border: 1px solid #ddd;
border-collapse: collapse;
}
th, td {
padding: 0.5em;
}
</style>
</head>
<body>
<h2>Books</h2>
<table>
<tr><th>ISBN</th><th>Title</th><th>Price</th></tr>
<xsl:for-each select="books/book">
<tr>
<td><xsl:value-of select="isbn"/></td>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
文件 input/books.html.xslt
由于 XSLT 文档本身就是一个 XML 文档,我们也可以用 pXML 来定义它
[xsl:stylesheet (xmlns:xsl=http://www.w3.org/1999/XSL/Transform version=1.0)
[xsl:output (method=text)]
[xsl:template (match=/)
<html>
<head>
<title>Books</title>
<style>
table, th, td {
border: 1px solid #ddd;
border-collapse: collapse;
}
th, td {
padding: 0.5em;
}
</style>
</head>
<body>
<h2>Books</h2>
<table>
<tr><th>ISBN</th><th>Title</th><th>Price</th></tr>
[xsl:for-each (select=books/book)
<tr>
<td>[xsl:value-of (select=isbn)]</td>
<td>[xsl:value-of (select=title)]</td>
<td>[xsl:value-of (select=price)]</td>
</tr>
]
</table>
</body>
</html>
]
]
文件 input/books.html.pxslt
类 dev.pxml.core.utilities.XSLTTransformer
提供了 static
方法来转换数据。例如,我们可以用上面的 pXML XSLT 文档转换上面的 pXML 书籍数据,如下所示
XSLTTransformer.transformPXMLFileWithPXMLXSLTFile (
new File ( "input/books.pxml" ),
new File ( "input/books.pxslt" ),
new File ( "output/books.html" ) );
执行上述语句会创建文件 output/books.html,其内容如下
<html>
<head>
<title>Books</title>
<style>
table, th, td {
border: 1px solid #ddd;
border-collapse: collapse;
}
th, td {
padding: 0.5em;
}
</style>
</head>
<body>
<h2>Books</h2>
<table>
<tr><th>ISBN</th><th>Title</th><th>Price</th></tr>
<tr>
<td>978-0135957059</td>
<td>The Pragmatic Programmer: Your Journey to Mastery</td>
<td>41.41</td>
</tr>
<tr>
<td>978-0735619678</td>
<td>Code Complete: A Practical Handbook of Software Construction</td>
<td>45.32</td>
</tr>
<tr>
<td>978-0134685991</td>
<td>Effective Java</td>
<td>44.10</td>
</tr>
</table>
</body>
</html>
在 Web 浏览器中,结果看起来像这样
使用 PML 中的 XML 技术
上一篇文章中的《pXML 前身》章节解释说,pXML 语法源自 Practical Markup Language (PML)。PML 是一种用于创建网页文章和书籍的标记语言。
现在我们可以说 PML 使用 pXML 语法。它还支持 宽松解析,但内部 AST 以 pXML 格式存储。将来,本文介绍的 pXML 解析器将在 PML 中使用。因此,上一章中说明的所有 XML 技术都可以用于 PML 文档。
例如,可以
- 使用 XQuery 提取 PML 文档中的所有链接
- 使用 XML 转换器将所有链接保存到 CSV 文件中,该文件可以被任何语言编写的工具读取,以检查死链接。
- 创建消耗 PML 解析器生成的 AST 的过滤器,然后转换 AST(添加/删除/更改节点),然后再让 PML 生成 HTML 输出。
人们可以轻松想象用户将能够创建和共享各种有用的 PML 扩展。
解析器(读取器)
上一章展示了我们 可以做什么 用 pXML 解析器。现在我们将深入了解 它是如何工作的,以及您如何使用和自定义解析器来满足您自己的特定需求。
注意
基于事件
该解析器是 基于事件的。它读取 pXML 文档并生成事件流。解析器本身不对解析的数据执行任何操作。每种类型的事件(例如 onNodeStart, onNodeEnd
等)都由一个回调函数处理。所有回调函数都属于一个事件处理程序对象。在解析之前,客户端代码必须将一个事件处理程序对象传递给解析器。事件处理程序是一个接口,包含每种事件类型的一个回调函数。它定义为 如下
package dev.pxml.core.reader.parser.eventHandler;
import dev.pxml.core.data.node.PXMLNode;
import dev.pxml.core.reader.reader.TextLocation;
import dev.pxml.core.utilities.annotations.NotNull;
public interface IParserEventsHandler<N, R> {
void onStart() throws Exception;
void onStop() throws Exception;
N onRootNodeStart ( @NotNull PXMLNode rootNode ) throws Exception;
void onRootNodeEnd ( N rootNode ) throws Exception;
N onNodeStart ( @NotNull PXMLNode node, @NotNull N parentNode ) throws Exception;
void onNodeEnd ( N node ) throws Exception;
void onText ( @NotNull String text, @NotNull N parentNode, TextLocation location )
throws Exception;
// [- and -] is included in comment
void onComment ( String comment, @NotNull N parentNode, TextLocation location )
throws Exception;
R getResult() throws Exception;
}
类型参数 N
定义了此事件处理程序生成的节点的类型。类型参数 R
定义了解析终止时创建的最终结果的类型。
核心库中包含以下 IParserEventsHandler
的实现
-
此处理程序创建一个标准的 Java
org.w3c.dom.Document
对象。它是上一章在验证或转换 pXML 文档时使用的处理程序。 -
除了创建
Document
对象之外,我们还可以使用此处理程序创建 pXML 特定的 AST。最终结果是一个 PXMLNode。 -
如果我们只需要将 pXML 转换为 XML,那么最有效的方法是使用此处理程序。而不是将整个 pXML 文档加载到内部树结构中,每个项目(
name
、attribute
、text
等)在解析后立即写入 JavaWriter
。因此,非常大的文档可以快速转换,而不会消耗内部内存。 -
这是一个实用程序处理程序,它将日志数据写入 Java
Writer
(默认是标准操作系统输出设备)。可用于调试目的。 -
顾名思义,此处理程序什么也不做。它在以下情况下很有用
-
我们只想知道解析器是否报告了错误(例如,格式错误的 pXML 文档)
-
我们不想处理所有事件。在这种情况下,我们可以创建一个继承自该处理程序的事件处理程序,并覆盖我们关心的函数。
-
-
此事件处理程序继承自
DoNothing_ParserEventHandler
,并覆盖onStart
和onEnd
函数以测量总解析时间。
自定义解析
如果以上处理程序都不适合您的需求,您可以创建一个实现 IParserEventsHandler
的类,并将其传递给实现 AEventStreamParser 的 解析器,从而创建自己的自定义事件处理程序。要开始,您可以查看上一章中提到的实现。
解析器使用 ITokenizer 来读取 pXML 令牌(名称、文本、注释等)。为了最大程度地定制,您可以提供自己的分词器和/或解析器,并将其与 pXML 的核心库一起使用。
解析器属性和功能
- 该解析器处于 概念验证 状态,尚未准备好投入生产使用。
- 用 Java 编写
- 免费且根据 MIT 许可证开源
- 无依赖
- 只有一个 +-55 KB 的 .jar 文件
- 快速(未使用正则表达式)
- 基于事件。因此内存占用低,即使读取大型文档也是如此。
- 可以提供自定义事件处理程序。增加了通用性
- 能够将 pXML 加载到标准的 Java
org.w3c.dom.Document
对象中。因此,可以使用所有基于Document
的 XML 技术(验证、查询、转换等)。 - 使用标准的 Java
Reader
/Writer
实现灵活的输入/输出配置
尚未支持的 XML 功能
当前实现中尚未支持以下功能
CDATA
部分- 处理指令
- DTD(由 XML Schema 替代;pXML 中不支持)
写入器
除了读取器之外,核心库还包含一个实现了 IPXMLWriter 接口的写入器。通过将标准的 Java java.io.Writer
传递给 PXMLWriter 类的构造函数来创建写入器。然后可以使用 writeEmptyNode
、writeTextNode
、writeComment
等方法将 pXML 写入任何目标(文件、字符串、URL 等)。写入器负责在需要时使用转义序列。
缩进必须手动完成。未来版本可能包含 美化打印 模式。
摘要
pXML 解析器可用于
- 读取 pXML 文档
- 将 pXML 转换为 XML
- 将 XML 转换为 pXML
- 在 pXML 文档中使用 XML 技术(验证、查询、修改和转换文档)
为了最大化通用性,解析器会生成一个事件流,该事件流可以被自定义事件处理程序消耗。
核心库还包含一个用于以编程方式编写 pXML 文档的写入器。
历史
- 2021 年 4 月 28 日:初始版本