跳转至

XML 函数

XML函数

本节中描述的函数以及类函数的表达式都在类型xml的值上操作。用于在值和类型xml之间转换的类函数的表达式xmlparsexmlserialize就不在这里重复介绍。使用大部分这些函数要求安装时使用了configure--with-libxml进行编译。

生成XML内容

一组函数和类函数的表达式可以用来从 SQL 数据产生 XML 内容。它们特别适合于将查询结果格式化成 XML 文档以便于在客户端应用中处理。

xmlcomment()
语法
xmlcomment(text)
描述
函数xmlcomment创建了一个 XML 值,它包含一个使用指定文本作为内容的 XML注释。该文本不包含“--”或者也不会以一个“-”结尾,这样结果的结构是一个合法的XML 注释。如果参数为空,结果也为空。
示例

``` sql SELECT xmlcomment('hello');

xmlcomment

```

xmlconcat()
语法
xmlconcat(xml[, ...])
描述
函数xmlconcat将由单个 XML 值组成的列表串接成一个单独的值,这个值包含一个 XML内容片断。空值会被忽略,只有当没有参数为非空时结果才为空。
示例
``` sql SELECT xmlconcat('', 'foo');
  xmlconcat

foo 如果 XML 声明存在,它们会按照下面的方式被组合。如果所有的参数值都有相同的 XML 版本声明,该版本将被用在结果中,否则将不使用版本。如果所有参数值有独立声明值“yes”,那么该值将被用在结果中。如果所有参数值都有一个独立声明值并且至少有一个为“no”,则“no”被用在结果中。否则结果中将没有独立声明。如果结果被决定要要求一个独立声明但是没有版本声明,将会使用一个版本1.0 的版本声明,因为 XML 要求一个 XML 声明要包含一个版本声明。编码声明会被忽略并且在所有情况中都会被移除。 例子: sql SELECT xmlconcat('<?xml version="1.1"?>', '<?xml version="1.1" standalone="no"?>'); xmlconcat


<?xml version="1.1"?> ```

xmlelement()
语法
xmlelement(name name [, xmlattributes(value [AS attname] [, ... ])] [, content, ...])
描述
表达式xmlelement使用给定名称、属性和内容产生一个 XML 元素。
示例

``` sql SELECT xmlelement(name foo);

xmlelement

SELECT xmlelement(name foo, xmlattributes('xyz' as bar));

xmlelement

SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');

         xmlelement

content 不是合法 XML 名字的元素名和属性名将被逃逸,逃逸的方法是将违反的字符用序列 *`_xHHHH_`* 替换,其中 *`HHHH`* 是被替换字符的Unicode 代码点的十六进制表示。例如: sql SELECT xmlelement(name "foo$bar", xmlattributes('xyz' as "a&b")); xmlelement


```

如果属性值是一个列引用,则不需要指定一个显式的属性名,在这种情况下列的名字将被默认用于属性的名字。在其他情况下,属性必须被给定一 个显式名称。因此这个例子是合法的:

CREATE TABLE test (a xml, b xml);
SELECT xmlelement(name test, xmlattributes(a, b)) FROM test;

但是下面这些不合法:

SELECT xmlelement(name test, xmlattributes('constant'), a, b) FROM test;
SELECT xmlelement(name test, xmlattributes(func(a, b))) FROM test;

如果指定了元素内容,它们将被根据其数据类型格式化。如果内容本身也是类型xml,就可以构建复杂的 XML 文档。例如:

SELECT xmlelement(name foo, xmlattributes('xyz' as bar),
                            xmlelement(name abc),
                            xmlcomment('test'),
                            xmlelement(name xyz));

                  xmlelement
----------------------------------------------
 <foo bar="xyz"><abc/><!--test--><xyz/></foo>

其他类型的内容将被格式化为合法的 XML 字符数据。这意味着字符 <, >, 和 &将被转换为实体。二进制数据(数据类型bytea)将被表示成base64或十六进制编码,具体取决于配置参数xmlbinary的设置。为了将SQL 和 SeaboxSQL 数据类型和 XML 模式声明对齐,我们期待单独数据类型的特定行为能够改进,到那时将会出现一个更为精确的描述。

xmlforest()
语法
xmlforest(content [AS name] [, ...])
描述
表达式xmlforest使用给定名称和内容产生一个元素的 XML 森林(序列)。
示例

``` sql SELECT xmlforest('abc' AS foo, 123 AS bar);

      xmlforest

abc123

SELECT xmlforest(table_name, column_name) FROM information_schema.columns WHERE table_schema = 'pg_catalog';

                                     xmlforest

pg_authidrolname pg_authidrolsuper … ```

如我们在第二个例子中所见,如果内容值是一个列引用,元素名称可以被忽略,这种情况下默认使用列名。否则,必须指定一个名字。

如上文xmlelement所示,非法 XML 名字的元素名会被逃逸。相似地,内容数据也会被逃逸来产生合法的 XML内容,除非它已经是一个xml类型。

注意如果 XML 森林由多于一个元素组成,那么它不是合法的 XML文档,因此在xmlelement中包装xmlforest表达式会有用处。

xmlpi()
语法
xmlpi(name target [, content])
描述
表达式xmlpi创建一个 XML 处理指令。如果存在内容,内容不能包含字符序列?>
示例
``` sql SELECT xmlpi(name php, 'echo "hello world";');
        xmlpi

<?php echo "hello world";?> ```

xmlroot()
语法
xmlroot(xml, version text | no value [, standalone yes|no|no value])
描述
表达式xmlroot修改一个 XML值的根结点的属性。如果指定了一个版本,它会替换根节点的版本声明中的值;如果指定了一个独立设置,它会替换根节点的独立声明中的值。
示例
``` sql SELECT xmlroot(xmlparse(document '<?xml version="1.1"?>abc'), version '1.0', standalone yes);
            xmlroot

<?xml version="1.0" standalone="yes"?> abc ```

xmlagg()
语法
xmlagg(xml)
描述
和这里描述的其他函数不同,函数xmlagg是一个聚集函数。它将聚集函数调用的输入值串接起来,非常像xmlconcat所做的事情,除了串接是跨行发生的而不是在单一行的多个表达式上发生。。
示例

``` sql CREATE TABLE test (y int, x xml); INSERT INTO test VALUES (1, 'abc'); INSERT INTO test VALUES (2, ''); SELECT xmlagg(x) FROM test; xmlagg


abc
为了决定串接的顺序,可以为聚集调用增加一个`ORDER BY`子句,如[聚集表达式](sql-expressions.md "聚集表达式")中所述。例如:

``` sql
SELECT xmlagg(x ORDER BY y DESC) FROM test;
        xmlagg
----------------------
 <bar/><foo>abc</foo>

不推荐使用下列非标准方法,但在特定情况下仍然有用:

SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
        xmlagg
----------------------
 <bar/><foo>abc</foo>

XML 谓词

这一节描述的表达式检查xml值的属性。

IS DOCUMENT
语法
xml IS DOCUMENT
描述
如果参数 XML 值是一个正确的 XML 文档,则IS DOCUMENT返回真,如果不是则返回假(即它是一个内容片断),或者是参数为空时返回空。文档和内容片断之间的区别请见XML类型。
IS NOT DOCUMENT
语法
xml IS NOT DOCUMENT
描述
如果参数中的XML值是一个正确的XML文档,那么表达式IS NOT DOCUMENT返回假,否则返回真(也就是说它是一个内容片段),如果参数为空则返回空。
XMLEXISTS()
语法
XMLEXISTS(text PASSING [BY REF] xml [BY REF])
描述
如果第一个参数中的 XPath 表达式返回任何结点,则函数xmlexists返回真,否则返回假(如果哪一个参数为空,则结果就为空)。
示例

``` sql SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'TorontoOttawa');

xmlexists

t (1 row) ```

BY REF子句在 SeaboxSQL 中没有作用,但是为了和其他实现保持 SQL 一致性和兼容性还是允许它出现。每一种 SQL标准,第一个BY REF是被要求的,而第二个则是可选的。也要注意 SQL 标准指定xmlexists结构来将一个XQuery 表达式作为第一个参数,但 SeaboxSQL 目前只支持 XPath,它是 XQuery的一个子集。

xml_is_well_formed()
语法
sql xml_is_well_formed(text) xml_is_well_formed_document(text) xml_is_well_formed_content(text)
描述

这些函数检查一个text串是不是一个良构的XML,返回一个布尔结果。

xml_is_well_formed_document检查一个良构的文档,而xml_is_well_formed_content检查良构的内容。如果GUC配置参数xmloption被设置为DOCUMENTxml_is_well_formed会做第一个函数的工作;如果配置参数被设置为CONTENTxml_is_well_formed会做第二个函数的工作。这意味着xml_is_well_formed对于检查一个到类型xml的简单造型是否会成功非常有用,而其他两个函数对于检查XMLPARSE的对应变体是否会成功有用。

示例

``` sql SET xmloption TO DOCUMENT; SELECT xml_is_well_formed('<>'); xml_is_well_formed


f (1 row)

SELECT xml_is_well_formed(''); xml_is_well_formed


t (1 row)

SET xmloption TO CONTENT; SELECT xml_is_well_formed('abc'); xml_is_well_formed


t (1 row)

SELECT xml_is_well_formed_document('bar'); xml_is_well_formed_document


t (1 row)

SELECT xml_is_well_formed_document('bar'); xml_is_well_formed_document


f (1 row) ```

最后一个例子显示了这些检查也包括名字空间是否正确地匹配。

处理 XML

要处理数据类型xml的值, SeaboxSQL 提供了函数xpathxpath_exists,它们计算 XPath 1.0表达式以及XMLTABLE表函数。

xpath()
语法
xpath(xpath, xml [, nsarray])
描述

函数xpath在 XML 值 xml 上计算 XPath 表达式 xpath (a text value)。它返回一个XML 值的数组,该数组对应于该 XPath 表达式产生的结点集合。如果该 XPath表达式返回一个标量值而不是一个结点集合,将会返回一个单一元素的数组。

第二个参数必须是一个良构的 XML 文档。特殊地,它必须有一个单一根结点元素。

该函数可选的第三个参数是一个名字空间映射的数组。这个数组应该是一个二维text数组,其第二轴长度等于2(即它应该是一个数组的数组,其中每一个都由刚好2 个元素组成)。每个数组项的第一个元素是名字空间的名称(别名),第二个元素是名字空间的 URI。并不要求在这个数组中提供的别名和在 XML文档本身中使用的那些名字空间相同(换句话说,在 XML文档中和在xpath函数环境中,别名都是本地的)。

示例

``` sql SELECT xpath('/my:a/text()', 'test', ARRAY[ARRAY['my', 'http://example.com']]);

xpath

{test} (1 row)

要处理默认(匿名)命名空间,做这样的事情:

``` screen
SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a>',
             ARRAY[ARRAY['mydefns', 'http://example.com']]);

 xpath
--------
 {test}
(1 row)

xpath_exists()
语法
xpath_exists(xpath, xml [, nsarray])
描述
函数xpath_existsxpath函数的一种特殊形式。这个函数不是返回满足 XPath 的单一 XML值,它返回一个布尔值表示查询是否被满足。这个函数等价于标准的XMLEXISTS谓词,不过它还提供了对一个名字空间映射参数的支持。
示例

``` sql SELECT xpath_exists('/my:a/text()', 'test', ARRAY[ARRAY['my', 'http://example.com']]);

xpath_exists

t (1 row) ```

xmltable()
语法
sql xmltable( [XMLNAMESPACES(namespace uri AS namespace name[, ...]), ] row_expression PASSING [BY REF] document_expression [BY REF] COLUMNS name { type [PATH column_expression] [DEFAULT default_expression] [NOT NULL | NULL] | FOR ORDINALITY } [, ...] )
描述

xmltable函数基于给定的XML值产生一个表、一个抽取行的XPath过滤器以及一个可选的列定义集合。

可选的XMLNAMESPACES子句是一个逗号分隔的名字空间列表。它指定文档中使用的XML名字空间极其别名。当前不支持默认的名字空间说明。

必需的row_expression参数是一个XPath表达式,该表达式会根据所提供的XML文档进行计算来得到一个有序的XML节点序列。xmltable会把这个序列转变成输出行。

document_expression 提供要操作的XML文档。BY REF子句在SeaboxSQL中没有效果,允许它的目的是为了遵守SQL以及与其他实现相兼容。该参数必须是一个结构良好的XML文档,不接受XML片段或者森林。

强制需要的COLUMNS子句指定输出表中的列列表。如果COLUMNS子句被省略,结果集合中的行包含类型为xml的单一列,列中包含匹配 row_expression 的数据。如果指定了COLUMNS,则每一项描述一个列。格式请见上面的语法综述。列名和类型是必需的,路径、默认值以及为空性子句是可选的。

被标记为FOR ORDINALITY的列将被行号填充,它们匹配输出列出现在原始的输入XML文档中的顺序。最多只能有一个列被标记为FOR ORDINALITY

一个列的column_expression是一个要针对每行(与 row_expression 的结果有关)计算的XPath表达式,它用来得到该列的值。如果没有给出column_expression,则把列名用作一种隐式路径。

如果一个列的XPath表达式返回多个元素,则会发生错误。如果该表达式匹配一个空标记,则结果是一个空字符串(不是NULL)。任何xsi:nil属性都会被忽略。

column_expression 匹配上的XML的文本主体被用作该列的值。一个元素中的多个text()节点会按照顺序串接起来。任何子元素、处理指令以及注释都会被忽略,但是子元素的文本内容会被串接到结果中。注意,两个非文本元素之间的仅有空格的text()节点会被保留,并且text()节点上的前导空格不会被平面化。

如果路径表达式不匹配一个给定行但制定有 default_expression ,则会使用从该表达式计算出的结果值。如果没有对该列给出DEFAULT子句,则该字段将被设置为NULLdefault_expression 可以引用在列列表中出现在它前面的输出列值,因此一列的默认值可能会基于另一列的值。

列可能会被标记为NOT NULL。如果一个NOT NULL列的 column_expression 不匹配任何东西并且没有DEFAULT或者 default_expression 也计算为空,则会报告一个错误。

和常规函数不同,column_expressiondefault_expression 在调用前不会被计算为简单值。 column_expression 通常为每一个输入行计算正好一次,default_expression 则在每当一个字段需要默认值时都会被计算。如果表达式是稳定的或者不变的,则重复计算可以被跳过。实际上xmltable的行为更像一个子查询而不是函数调用。这意味着你可以在 default_expression 中使用易变函数,并且 column_expression 可以基于XML文档的其他部分。

示例

``` sql CREATE TABLE xmldata AS SELECT xml $$ AU Australia JP Japan Shinzo Abe 145935 SG Singapore 697 $$ AS data;

SELECT xmltable.* FROM xmldata, XMLTABLE('//ROWS/ROW' PASSING data COLUMNS id int PATH '@id', ordinality FOR ORDINALITY, "COUNTRY_NAME" text, country_id text PATH 'COUNTRY_ID', size_sq_km float PATH 'SIZE[@unit = "sq_km"]', size_other text PATH 'concat(SIZE[@unit!="sq_km"], " ", SIZE[@unit!="sq_km"]/@unit)', premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') ;

id | ordinality | COUNTRY_NAME | country_id | size_sq_km | size_other | premier_name
----+------------+--------------+------------+------------+--------------+--------------- 1 | 1 | Australia | AU | | | not specified 5 | 2 | Japan | JP | | 145935 sq_mi | Shinzo Abe 6 | 3 | Singapore | SG | 697 | | not specified

接下来的例子展示了多个text()节点的串接、列名用作XPath过滤器的用法以及对空格、XML注释和处理指令的处理:

``` sql
CREATE TABLE xmlelements AS SELECT
xml $$
  <root>
   <element>  Hello<!-- xyxxz -->2a2<?aaaaa?> <!--x-->  bbb<x>xxx</x>CC  </element>
  </root>
$$ AS data;

SELECT xmltable.*
  FROM xmlelements, XMLTABLE('/root' PASSING data COLUMNS element text);
       element        
----------------------
   Hello2a2   bbbCC  

下面的例子展示了如何使用XMLNAMESPACES子句指定用在XML文档以及XPath表达式中的名字空间列表:

WITH xmldata(data) AS (VALUES ('
<example xmlns="http://example.com/myns" xmlns:B="http://example.com/b">
 <item foo="1" B:bar="2"/>
 <item foo="3" B:bar="4"/>
 <item foo="4" B:bar="5"/>
</example>'::xml)
)
SELECT xmltable.*
  FROM XMLTABLE(XMLNAMESPACES('http://example.com/myns' AS x,
                              'http://example.com/b' AS "B"),
             '/x:example/x:item'
                PASSING (SELECT data FROM xmldata)
                COLUMNS foo int PATH '@foo',
                  bar int PATH '@B:bar');
 foo | bar
-----+-----
   1 |   2
   3 |   4
   4 |   5
(3 rows)

将表映射到XML(table_to_xml()/query_to_xml()/cursor_to_xml())

语法
sql table_to_xml(tbl regclass, nulls boolean, tableforest boolean, targetns text) query_to_xml(query text, nulls boolean, tableforest boolean, targetns text) cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, targetns text)
描述

上面的函数将会把关系表的内容映射成 XML 值。它们可以被看成是 XML 导出功能, 每一个函数的返回值都是xml

table_to_xml映射由参数 tbl 传递的命名表的内容。regclass类型接受使用常见标记标识表的字符串,包括可选的模式限定和双引号。query_to_xml执行由参数 query 传递的查询并且映射结果集。cursor_to_xmlcursor 指定的游标中取出指定数量的行。如果需要映射一个大型的表,我们推荐这种变体,因为每一个函数都是在内存中构建结果值的。

如果 tableforest 为假,则结果的 XML 文档看起来像这样:

<tablename>
  <row>
    <columnname1>data</columnname1>
    <columnname2>data</columnname2>
  </row>

  <row>
    ...
  </row>

  ...
</tablename>

如果 tableforest 为真,结果是一个看起来像这样的 XML 内容片断:

<tablename>
  <columnname1>data</columnname1>
  <columnname2>data</columnname2>
</tablename>

<tablename>
  ...
</tablename>

...

如果没有表名可用,在映射一个查询或一个游标时,在第一种格式中使用串table,在第二种格式中使用row

这几种格式的选择由用户决定。第一种格式是一个正确的 XML文档,它在很多应用中都很重要。如果结果值要被重组为一个文档,第二种格式在cursor_to_xml函数中更有用。前文讨论的产生XML 内容的函数(特别是xmlelement)可以被用来把结果修改成符合用户的要求。

数据值会被以前文的函数xmlelement中描述的相同方法映射。

参数 nulls 决定空值是否会被包含在输出中。如果为真,列中的空值被表示为:

<columnname xsi:nil="true"/>

其中xsi是 XML 模式实例的 XML 名字空间前缀。一个合适的名字空间声明将被加入到结果值中。如果为假,包含空值的列将被从输出中忽略掉。

参数 targetns 指定想要的结果的 XML 名字空间。如果没有想要的特定名字空间,将会传递一个空串。

下面的函数返回 XML 模式文档,这些文档描述上述对应函数所执行的映射:

table_to_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text)
query_to_xmlschema(query text, nulls boolean, tableforest boolean, targetns text)
cursor_to_xmlschema(cursor refcursor, nulls boolean, tableforest boolean, targetns text)

最重要的是相同的参数被传递来获得匹配的 XML 数据映射和 XML 模式文档。

示例

下面的函数产生 XML 数据映射和对应的 XML模式,并把产生的结果链接在一起放在一个文档(或森林)中。在要求自包含和自描述的结果是它们非常有用:

table_to_xml_and_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text)
query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targetns text)

另外,下面的函数可用于产生相似的整个模式或整个当前数据库的映射:

schema_to_xml(schema name, nulls boolean, tableforest boolean, targetns text)
schema_to_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text)
schema_to_xml_and_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text)

database_to_xml(nulls boolean, tableforest boolean, targetns text)
database_to_xmlschema(nulls boolean, tableforest boolean, targetns text)
database_to_xml_and_xmlschema(nulls boolean, tableforest boolean, targetns text)

注意这些函数可能产生很多数据,它们都需要在内存中被构建。在请求大型模式或数据库的内容映射时,可以考虑分别映射每一个表,甚至通过一个游标来映射。

一个模式内容映射的结果看起来像这样:

<schemaname>

table1-mapping

table2-mapping

...

</schemaname>

其中一个表映射的格式取决于上文解释的 tableforest 参数。

一个数据库内容映射的结果看起来像这样:

<dbname>

<schema1name>
  ...
</schema1name>

<schema2name>
  ...
</schema2name>

...

</dbname>

其中的模式映射如上所述。

作为一个使用这些函数产生的输出的例子,下文展示了一个XSLT 样式表,它将table_to_xml_and_xmlschema的输出转换为一个包含表数据的扁平转印的 HTML文档。以一种相似的方式,这些函数的结果可以被转换成其他基于 XML 的格式。

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.w3.org/1999/xhtml"
>

  <xsl:output method="xml"
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
      doctype-public="-//W3C/DTD XHTML 1.0 Strict//EN"
      indent="yes"/>

  <xsl:template match="/*">
    <xsl:variable name="schema" select="//xsd:schema"/>
    <xsl:variable name="tabletypename"
                  select="$schema/xsd:element[@name=name(current())]/@type"/>
    <xsl:variable name="rowtypename"
                  select="$schema/xsd:complexType[@name=$tabletypename]/xsd:sequence/xsd:element    [@name='row']/@type"/>

    <html>
      <head>
        <title><xsl:value-of select="name(current())"/></title>
      </head>
      <body>
        <table>
          <tr>
            <xsl:for-each select="$schema/xsd:complexType[@name=$rowtypename]/xsd:sequence/xsd:element/    @name">
              <th><xsl:value-of select="."/></th>
            </xsl:for-each>
          </tr>

          <xsl:for-each select="row">
            <tr>
              <xsl:for-each select="*">
                <td><xsl:value-of select="."/></td>
              </xsl:for-each>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>