ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 9.14\. XML 函数 在本节描述的函数和像函数的表达式操作都是基于`xml`类型的值。 查看[Section 8.13](#calibre_link-1460)获取关于`xml`类型的信息。 像函数表达式的`xmlparse`和`xmlserialize` 用来转换为和从类型`xml`转换,不在这里重复。 使用这些函数需要安装与配置了`configure --with-libxml`。 ## 9.14.1\. 生成XML内容 一组函数和像函数的表达式可用于从SQL数据生成XML内容。 所以它们特别适合于查询结果格式化成在客户端应用程序处理的XML文件。 ### 9.14.1.1\. `xmlcomment` ``` xmlcomment(_text_) ``` `xmlcomment`函数创建一个包含XML注释的特定文本内容的值。 文本中不能包含"`--`"或以"`-`" 的结束,这样的文本是有效的XML注释。如果参数是空,结果是空。 例子: ``` SELECT xmlcomment('hello'); xmlcomment -------------- <!--hello--> ``` ### 9.14.1.2\. `xmlconcat` ``` xmlconcat(_xml_[, ...]) ``` 函数`xmlconcat`连接一个独立的XML值列表来创建一个包含XML内容片段的单值。 忽略空值;只有当参数都为空时结果是空。 例子: ``` SELECT xmlconcat('<abc/>', '<bar>foo</bar>'); xmlconcat ---------------------- <abc/><bar>foo</bar> ``` XML声明,如果存在,结合如下。如果所有参数使用相同的XML版本声明,则在结果中使用版本。 否则不用版本。如果所有的参数值有独立的声明值"yes", 然后这个值在结果里使用。如果所有的参数值有独立的声明,并且至少有一个是"no", 然后这个值在结果里使用。否则结果将没有独立声明。如果结果决定需要一个独立的声明, 但没有声明版本,将使用一个带有版本1.0的版本声明,因为XML需要一个XML声明包含版本声明。 忽略并且在所有情况下删除编码声明。 例子: ``` SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone="no"?><bar/>'); xmlconcat ----------------------------------- <?xml version="1.1"?><foo/><bar/> ``` ### 9.14.1.3\. `xmlelement` ``` xmlelement(name _name_ [, xmlattributes(`_value_` [AS `_attname_`] [, ... ])] [`_, content, ..._`]) ``` `xmlelement`表达式生成一个带有给定名称,属性和内容的XML元素。 例子: ``` SELECT xmlelement(name foo); xmlelement ------------ <foo/> SELECT xmlelement(name foo, xmlattributes('xyz' as bar)); xmlelement ------------------ <foo bar="xyz"/> SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent'); xmlelement ------------------------------------- <foo bar="2007-01-26">content</foo> ``` 不是有效的XML元素和属性名的名称由序列`_x``_HHHH_`_ 替换有问题的字符逃逸,这里的`_HHHH_` 是字符的16进制形式的Unicode代码点。例如: ``` SELECT xmlelement(name "foo$bar", xmlattributes('xyz' as "a&b")); xmlelement ---------------------------------- <foo_x0024_bar a_x0026_b="xyz"/> ``` 如果属性值是一个列引用则不用指定明确的属性名称,在这种情况下,列的名称将默认为属性名。 在其它情况下,属性必须给予一个明确的名称。因此,这个例子是有效的: ``` 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字符串数据。这意味着特殊的字符&lt;, &gt;, 和&将转化为实体。二进制数据(`bytea`数据类型) 将用base64或16进制编码表示,取决于配置参数[xmlbinary](#calibre_link-2226)的设置。 单个数据类型的特定行为预计将发展为了使SQL和PostgreSQL数据类型和XML架构规范一致, 到时将出现更准确描述。 ### 9.14.1.4\. `xmlforest` ``` xmlforest(_content_ [AS `_name_`] [, ...]) ``` `xmlforest`表达式生成一个使用指定的名称和内容的XML 森林(序列)元素。 示例: ``` SELECT xmlforest('abc' AS foo, 123 AS bar); xmlforest ------------------------------ <foo>abc</foo><bar>123</bar> SELECT xmlforest(table_name, column_name) FROM information_schema.columns WHERE table_schema = 'pg_catalog'; xmlforest ------------------------------------------------------------------------------------------- <table_name>pg_authid</table_name><column_name>rolname</column_name> <table_name>pg_authid</table_name><column_name>rolsuper</column_name> ... ``` 在第二个例子可以看出,如果内容值为列引用,元素名称可以省略。在这种情况下, 默认使用列名。否则,必须指定名称。 非法XML名的元素名称,像上面的`xmlelement`逃逸处理。 类似的,内容数据逃逸生成有效的XML内容, 除非它已经是`xml`类型的。 请注意,如果包含一个以上的元素,XML的森林不是有效的XML文档, 所以在`xmlelement`里面封装`xmlforest`表达式可能是有用的。 ### 9.14.1.5\. `xmlpi` ``` xmlpi(name _target_ [, `_content_`]) ``` `xmlpi`表达式创建一条XML处理指令。 如果存在,内容必须不能包含字符序列`?&gt;`。 示例: ``` SELECT xmlpi(name php, 'echo "hello world";'); xmlpi ----------------------------- <?php echo "hello world";?> ``` ### 9.14.1.6\. `xmlroot` ``` xmlroot(_xml_, version _text_ | no value [, standalone yes|no|no value]) ``` `xmlroot`更改XML值的根节点属性。如果指定一个版本, 它替换根节点的版本声明值;如果指定一个standalone设置,它替换根节点的standalone声明值。 ``` SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'), version '1.0', standalone yes); xmlroot ---------------------------------------- <?xml version="1.0" standalone="yes"?> <content>abc</content> ``` ### 9.14.1.7\. `xmlagg` ``` xmlagg(_xml_) ``` 不像这里描述的其它函数,函数`xmlagg`是一个聚集函数。 它连接聚集函数调用的输入值,很像`xmlconcat`, 除了连接发生在多行而不是发生在多个单行的表达式。 请参阅[Section 9.20](#calibre_link-1641)获取关于聚集函数的更多信息。 示例: ``` CREATE TABLE test (y int, x xml); INSERT INTO test VALUES (1, '<foo>abc</foo>'); INSERT INTO test VALUES (2, '<bar/>'); SELECT xmlagg(x) FROM test; xmlagg ---------------------- <foo>abc</foo><bar/> ``` 为了确定连接顺序,要添加一个`ORDER BY`子句到聚合调用, 描述在[Section 4.2.7](#calibre_link-1070)。示例: ``` 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> ``` ## 9.14.2\. XML Predicates 这节描述的表达式检查`xml`值的属性。 ### 9.14.2.1\. `IS DOCUMENT` ``` _xml_ IS DOCUMENT ``` 如果参数XML值是一个合法的XML文档,表达式`IS DOCUMENT`返回真。 否则返回假(例如,内容片段)或如果参数为空则返回空。请参阅[Section 8.13](#calibre_link-1460) 获取关于文档和内容片段之间的不同。 ### 9.14.2.2\. `XMLEXISTS` ``` XMLEXISTS(_text_ PASSING [BY REF] _xml_ [BY REF]) ``` 如果第一个参数中的XPath表达式返回任何节点,那么函数`xmlexists`返回真, 否则返回假。(如果其他参数是null,结果是null。) 示例: ``` SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Toronto</town><town>Ottawa</town></towns>'); xmlexists ------------ t (1 row) ``` 在PostgreSQL中,`BY REF`子句没有影响, 但是为了与SQL的一致性和其他实现的兼容性是允许的。SQL标准中, 第一个`BY REF`是必须的,第二个`BY REF`是可选的。 也请注意,SQL标准声明`xmlexists`构造接受XQuery表达式作为第一个参数, 但是PostgreSQL目前只接受XQuery的一个子集XPath。 ### 9.14.2.3\. `xml_is_well_formed` ``` 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`检查格式良好的内容。 `xml_is_well_formed`如果[xmloption](#calibre_link-2227)参数设置为 `DOCUMENT`则检查文档,如果设置为`CONTENT`则检查内容。 这意味着`xml_is_well_formed`有助于看到一个简单到类型`xml` 的转换是否会成功,而另外两个函数有助于看到相应的`XMLPARSE`变体是否会成功。 示例: ``` SET xmloption TO DOCUMENT; SELECT xml_is_well_formed('<>'); xml_is_well_formed -------------------- f (1 row) SELECT xml_is_well_formed('<abc/>'); 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('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</pg:foo>'); xml_is_well_formed_document ----------------------------- t (1 row) SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</my:foo>'); xml_is_well_formed_document ----------------------------- f (1 row) ``` 最后一个示例显示了检查包括命名空间是否正确匹配。 ## 9.14.3\. 处理XML PostgreSQL提供了`xpath`和`xpath_exists` 函数处理`xml`数据类型的值,计算XPath 1.0表达式的结果。 ``` xpath(_xpath_, _xml_ [, `_nsarray_`]) ``` `xpath`函数,对XML值`_xml_` 计算XPath表达式`_xpath_`(`text`值)的结果。 它返回一个XML值的数组对应XPath表达式所产生的节点集。 如果XPath表达式返回一个标量值而不是节点集,那么返回一个单个元素的数组。 第二个参数必须是一个完整的XML文档。特别是,它必须有一个根节点元素。 该函数的第三个参数是一个命名空间的数组映射。这个数组应该是一个两维`text`数组, 第二个维的长度等于2(它应该是一个数组的数组,其中每个正好包含2个元素)。 每个数组项的第一个元素是命名空间名称的别名,第二个元素是命名空间 URI。 这个数组的别名不是必须提供的,与在XML文档本身使用的相同。(换句话说, 在XML文档和在`xpath`函数的上下文中,别名是_local_)。 示例: ``` SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>', ARRAY[ARRAY['my', 'http://example.com']]); xpath -------- {test} (1 row) ``` 处理默认的命名空间,像下面这样做: ``` 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_, _xml_ [, `_nsarray_`]) ``` `xpath_exists`函数是`xpath`函数的一种特殊化形式。 这个函数返回一个布尔值表明是否满足这个查询,而不是返回满足XPath的单个XML值。 这个函数相当于标准的`XMLEXISTS`,除了它还对命名空间映射参数提供支持。 示例: ``` SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>', ARRAY[ARRAY['my', 'http://example.com']]); xpath_exists -------------- t (1 row) ``` ## 9.14.4\. 到XML的映射表 下面的函数映射关系表的内容到XML值。可以将它们认为XML导出功能: ``` 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`。 `table_to_xml`映射命名表的内容,作为参数`tbl`传递。 `regclass`类型接受使用常用符号的字符串标识表,包括可选的模式资格和双引号。 `query_to_xml`执行查询,这个查询的文本作为`query` 参数传递,并映射结果集。`cursor_to_xml`从参数`cursor` 指定的游标中获取指定数量的行。如果大数据表需要映射,建议使用这个变体, 因为结果值是通过每个函数在内存中构建的。 如果`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> ``` 模式映射如上所述。 使用这些函数产生的输出作为例子,[Figure 9-1](#calibre_link-2228)显示一个XSLT样式表转换 `table_to_xml_and_xmlschema`的输出到HTML文档, 该文档中包含了一个表数据的表格格式副本。以类似的方式, 这些函数的结果可以转换成其它基于XML的格式。 **Figure 9-1\. XSLT样式表--将SQL/XML输出转换成HTML** ``` <?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> ```