跳到主要内容

XML 类型

PostgreSQL 的 xml 类型保存格式良好的 XML 文档或内容片段,并提供 XPath 查询函数。它适合兼容已有 XML 协议、配置和交换格式;新应用若没有 XML 互操作需求,不应为了“结构化”而默认选择它。

文档与内容片段​

xml 可以配置为只接受完整文档,也可以接受 XML 内容片段:

CREATE TABLE messages (
message_id bigint PRIMARY KEY,
document xml NOT NULL
);

INSERT INTO messages VALUES
(1, '<order id="A-1"><total>12.50</total></order>');

输入会检查 XML 是否格式良好,但不会自动验证 XSD、业务字段、签名或外部实体策略。格式正确不等于业务有效。

XPath 查询​

SELECT xpath('/order/@id', document)
FROM messages;

SELECT xpath('/order/total/text()', document)
FROM messages;

xpath 返回 xml[],即使只有一个结果也可能是数组;应用或 SQL 需要明确取元素并转换为目标类型。XPath 表达式和 XML 内容都应参数化或经过可信构造,不要通过字符串拼接接受任意查询表达式。

命名空间​

带命名空间的 XML 查询需要提供前缀映射:

SELECT xpath(
'/p:order/p:total/text()',
'<p:order xmlns:p="urn:example"><p:total>12.50</p:total></p:order>',
ARRAY[ARRAY['p', 'urn:example']]
);

XPath 中的前缀只是查询映射,不要求与文档中实际使用的前缀文字相同;URI 才是命名空间身份。

编码、实体和安全​

XML 可能包含实体、外部资源和大型嵌套结构。解析来自不可信来源的 XML 时,应在应用和数据库驱动层限制实体解析、大小和深度,防范 XXE、实体扩展和资源耗尽。PostgreSQL 的类型检查不能替代完整的安全策略。

XML 文本的字符编码在进入数据库前通常已转换为服务器编码。需要保留原始字节、签名或精确序列化格式时,应额外保存原始 bytea 或哈希。

索引与选型​

xml 没有像 JSONB 那样通用的 GIN 索引。频繁查询的字段应抽取为普通列、生成列或辅助表并建立合适索引:

ALTER TABLE messages
ADD COLUMN order_id text
GENERATED ALWAYS AS ((xpath('/order/@id', document))[1]::text) STORED;

复杂 XML 搜索可考虑应用层解析或专门 XML 数据库。不要每次查询都扫描并解析大量文档后再判断。

常见误区​

  • 把 xml 的格式校验当成 XSD 或业务校验。
  • 忽略命名空间,使用看似正确但匹配不到节点的 XPath。
  • 把 XML 当任意文本存储,无法利用结构查询,却承受了解析成本。
  • 直接展示未经转义的 XML 内容,造成 HTML/XML 注入风险。
  • 没有为大文档限制大小、深度和解析时间。