F.46. xml2

xml2模块提供 XPath 查询和 XSLT 功能。

F.46.1. 废弃公告

PostgreSQL 8.3 开始,在核心服务器中就已经有基于 SQL/XML 标准的 XML 相关功能。那些功能覆盖了 XML 语法检查和 XPath 查询,这些本模块也能做,但是其 API 并不是完全兼容。这个模块已经有计划将从 PostgreSQL 的一个未来版本中移除,因此我们鼓励你尝试转换你的应用。如果你发现这个模块的某些功能在更新的 API 中没有合适的形式相对应,请向表达你的问题,这样该缺点会被进行改进。

F.46.2. 函数的描述

Table F-35展示了这个模块提供的函数。这些函数提供了直接的 XML 解析和 XPath 查询。所有参数都是text类型,因此为了简洁都没有被显示。

Table F-35. 函数

函数返回描述
xml_is_well_formed(document) bool

这个函数解析其参数中的文档文本并且在该文档是一个结构良好的 XML 时返回真(注意,在 PostgreSQL 8.2 之前,这个函数被称为xml_valid()。这是错误的名称,因为在 XML 中有效性和结构良好性具有不同的含义。旧的名称仍然有效,但是已被启用。)。

xpath_string(document, query) text

这些函数在提供的文档上计算 XPath 查询,并且将结果造型为指定的类型。

xpath_number(document, query) float4
xpath_bool(document, query) bool
xpath_nodeset(document, query, toptag, itemtag) text

这个函数在文档上计算查询并且把结果包装在 XML 标签中。如果结果是多值的,输出看起来是这样:

<toptag>
<itemtag>Value 1 which could be an XML fragment</itemtag>
<itemtag>Value 2....</itemtag>
</toptag>

如果toptag或者itemtag是一个空字符串,相关的标签会被忽略。

xpath_nodeset(document, query) text

xpath_nodeset(document, query, toptag, itemtag)相似但是结果忽略两种标签。

xpath_nodeset(document, query, itemtag) text

xpath_nodeset(document, query, toptag, itemtag)相似但是结果忽略toptag

xpath_list(document, query, separator) text

这个函数返回多个值,并且用指定的分隔符分隔,例如分隔符是,,结果就是Value 1,Value 2,Value 3

xpath_list(document, query) text 这是上面函数的一个包装器,它用,作为分隔符。

F.46.3. xpath_table

xpath_table(text key, text document, text relation, text xpaths, text criteria) returns setof record

xpath_table是一个表函数,它在一组文档中的每一个上计算一组 XPath 查询,并且将结果作为一个表返回。来自原始文档表的主键域被返回为结果的第一列,这样结果集可以被用于连接。其参数在Table F-36中描述。

Table F-36. xpath_table 参数

参数描述
key

"key"域的名称 — 这只是被用作输出表中第一列的域,即它标识每一个输出行是来自于哪个记录(见下面有关多个值的注解)

document

包含 XML 文档的域的名称

relation

包含文档的表或视图的名称

xpaths

一个或多个 XPath 表达式,用|分隔

criteria

WHERE 子句的内容。这不能被忽略,因此如果你想要处理关系中的所有行,可以使用true1=1

这些参数(除了 XPath 字符串)只是会被替换到一个纯粹的 SQL SELECT 语句中,因此你有一些灵活性 — 该语句是

SELECT <key>, <document> FROM <relation> WHERE <criteria>

因此那些参数可以是那些特定位置上合法的任何东西。来自于这个 SELECT 的结果需要返回正好两列(它确实会这样,除非你尝试为键或文档列出多个域)。注意这种简单方法要求你验证任何用户提供的值来避免 SQL 注入攻击。

该函数必须被使用在一个FROM表达式中,并带有一个AS子句来指定输出列,例如

SELECT * FROM
xpath_table('article_id',
            'article_xml',
            'articles',
            '/article/author|/article/pages|/article/title',
            'date_entered > ''2003-01-01'' ')
AS t(article_id integer, author text, page_count integer, title text);

AS子句定义了输出表中列的名称和类型。第一个是"key"域并且剩下的对应于 XPath 查询。如果 XPath 查询比结果列多,额外的查询将被忽略。如果结果列比 XPath 查询多,额外的列将是 NULL。

注意这个例子定义page_count结果列为一个整数。该函数在内部处理字符串表达,因此当你在输出中想要一个整数时,它将采用 XPath 结果的字符串表达并且使用 PostgreSQL 输入函数来把它转换成一个整数(或者AS子句要求的任何类型)。如果它无法做到这一点将会导致一个错误 — 例如结果是空 — 因此如果你认为你的数据有任何问题,你可能希望坚持用text作为列类型。

调用的SELECT语句不必只是 SELECT * — 它可以用名称引用输出列或者将它们连接到其他表。该函数会产生一个虚拟表,你可以在其上执行任何所需的操作(例如聚集、连接、排序等)。因此我们也可以有:

SELECT t.title, p.fullname, p.email
FROM xpath_table('article_id', 'article_xml', 'articles',
                 '/article/title|/article/author/@id',
                 'xpath_string(article_xml,''/article/@date'') > ''2003-03-20'' ')
       AS t(article_id integer, title text, author_id integer),
     tblPeopleInfo AS p
WHERE t.author_id = p.person_id;

作为一个更复杂的例子。当然,为了便利你也可以把所有这些包装在一个视图中。

F.46.3.1. 多值结果

xpath_table函数假定每一个 XPath 查询的结果可能是多值的,因此该函数返回的行数可能与输入文档的数目不同。被返回的第一行包含来自每一个查询的第一个结果,第二行则是来自每一个查询的第二个结果。如果其中一个查询的值比其他查询少,则会为它返回空值。

在某些情况下,一个用户将知道一个给定的 XPath 查询将只返回一个单一结果(可能是一个唯一文档标识符) — 如果和一个返回多值的 XPath 查询一起使用,单值结果将只出现在结果的第一行中。对于这种情况的解决方案是使用键域作为针对一个更简单 XPath 查询的连接的一部分。一个例子:

CREATE TABLE test (
    id int PRIMARY KEY,
    xml text
);

INSERT INTO test VALUES (1, '<doc num="C1">
<line num="L1"><a>1</a><b>2</b><c>3</c></line>
<line num="L2"><a>11</a><b>22</b><c>33</c></line>
</doc>');

INSERT INTO test VALUES (2, '<doc num="C2">
<line num="L1"><a>111</a><b>222</b><c>333</c></line>
<line num="L2"><a>111</a><b>222</b><c>333</c></line>
</doc>');

SELECT * FROM
  xpath_table('id','xml','test',
              '/doc/@num|/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c',
              'true')
  AS t(id int, doc_num varchar(10), line_num varchar(10), val1 int, val2 int, val3 int)
WHERE id = 1 ORDER BY doc_num, line_num

 id | doc_num | line_num | val1 | val2 | val3
----+---------+----------+------+------+------
  1 | C1      | L1       |    1 |    2 |    3
  1 |         | L2       |   11 |   22 |   33

要在每一行上得到doc_num,解决方案是使用xpath_table的两个调用并且连接结果:

SELECT t.*,i.doc_num FROM
  xpath_table('id', 'xml', 'test',
              '/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c',
              'true')
    AS t(id int, line_num varchar(10), val1 int, val2 int, val3 int),
  xpath_table('id', 'xml', 'test', '/doc/@num', 'true')
    AS i(id int, doc_num varchar(10))
WHERE i.id=t.id AND i.id=1
ORDER BY doc_num, line_num;

 id | line_num | val1 | val2 | val3 | doc_num
----+----------+------+------+------+---------
  1 | L1       |    1 |    2 |    3 | C1
  1 | L2       |   11 |   22 |   33 | C1
(2 rows)

F.46.4. XSLT 函数

如果安装了 libxslt,那么可以使用下列函数:

F.46.4.1. xslt_process

xslt_process(text document, text stylesheet, text paramlist) returns text

这个函数将 XSL 样式表应用于文档并且返回转换过的结果。paramlist是一个被用在转换中的参数赋值列表,以a=1,b=2的形式指定。注意参数解析是非常天真的:参数值不能包含逗号!

还有一个双参数版本的xslt_process,它不会向转换传递任何参数。

F.46.5. 作者

John Gray

这个模块的开发由 Torchbox Ltd. (www.torchbox.com) 赞助。它使用了和 PostgreSQL 相同的 BSD 许可证。