2. HL7 v2消息结构与编码规则

HL7 v2 的互操作性建立在一条非常朴素的设计哲学上:消息是纯文本,用一组可打印字符作为分隔符,把临床信息切成层层嵌套的层级。只要双方对分隔符和层级含义达成一致,任何系统都能用最简单的字符串解析方式读懂一条消息。理解这套编码规则,是接口引擎开发与排障的基本功——引擎里绝大多数"消息解析失败""字段错位""中文乱码"问题,最终都能追溯到分隔符或层级理解偏差。

一、从一条真实消息看层级

下面是一条典型的 ADT^A01(患者入院)消息片段,它同时展示了段、字段、组件、子组件四个层级:

MSH|^~\&|HIS|HOSP_A|LIS|HOSP_A|20240512093000||ADT^A01^ADT_A01|MSG00001|P|2.5.1
PID|1||P20240512001^^^HOSP_A^MR||张^三||19800115|M|||北京市朝阳区^朝阳路1号^^100020
PV1|1|I|WARD01^01^01^HOSP_A|||||1001^李^医生

这里出现了五个关键分隔符,它们全部定义在 MSH 段的前几个字符位置,是整条消息的"元信息":

  • 字段分隔符 |:MSH 第 1 字段(MSH-1),把段切成字段;
  • 组件分隔符 ^:MSH-2 的第一个字符,把字段切成组件;
  • 重复分隔符 ~:MSH-2 的第二个字符,表示同一字段可重复出现;
  • 转义符 \:MSH-2 的第三个字符,用于在数据中嵌入分隔符本身;
  • 子组件分隔符 &:MSH-2 的第四个字符,把组件再切成子组件。

注意 MSH 段本身是个特例:MSH-1 就是字段分隔符"|"本身,MSH-2 就是编码字符"^~\&",因此 MSH 的字段编号与其它段相比整体"错位"一位。这是新手最容易踩的坑——解析 MSH 时必须特殊处理,不能套用通用段解析逻辑。

二、四个层级的嵌套关系

HL7 v2 的数据模型是严格的树形嵌套:一条消息由若干段组成,段由字段组成,字段由组件组成,组件由子组件组成。以 PID-5(患者姓名)为例,其数据类型为 XPN(扩展人员姓名),结构为:

PID-5 = 张^三^^^张先生^^L
         │  │  │ │  │    └─ 组件7:名称类型代码
         │  │  │ │  └────── 组件6:前缀
         │  │  │ └───────── 组件5:后缀
         │  │  └─────────── 组件4:第二姓氏
         │  └────────────── 组件3:中间名
         └───────────────── 组件1:姓氏
                           组件2:名

再看一个含子组件的例子,PID-3(患者标识列表)数据类型为 CX:

PID-3 = P20240512001^^^HOSP_A^MR&医保&M10
         │           │      │  └─ 子组件:命名空间ID
         │           │      └──── 组件5:标识类型代码
         │           └─────────── 组件4:分配机构
         └─────────────────────── 组件1:ID号

组件 5 内部又用 & 切出"MR&医保&M10"三个子组件,分别表示标识类型、类型描述、编码系统。这种"组件套子组件"的设计让 HL7 v2 在不改变分隔符体系的前提下,承载了相当复杂的编码信息。

HL7 v2 消息层级与分隔符体系 消息 Message 由多个段按顺序组成,以 MSH 开头,以回车(\r)分隔各段 ADT^A01 段分隔符 \r 段 Segment 三个大写字母标识,如 MSH、PID、PV1、OBX PID 字段分隔符 | 字段 Field 按位置编号,如 PID-3、PID-5;可重复(~),可空 PID-5 组件分隔符 ^ 组件 Component 字段内的子结构,如 XPN 的姓、名、中间名、前缀、后缀 张^三^^^张先生 子组件分隔符 & 子组件 Subcomponent 组件内的进一步细分,如 CX 组件5 中的 MR&医保&M10 MR&医保&M10 编码字符(MSH-2): ^ 组件分隔符 ~ 重复分隔符 \ 转义符 & 子组件分隔符 | 字段分隔符(MSH-1)

三、数据类型决定字段的内部结构

字段不是随意填写的字符串,每个字段都绑定一个 HL7 数据类型,数据类型规定了它有几个组件、每个组件什么含义。常见类型包括:

数据类型含义典型字段组件数
ST字符串PID-8 性别1
CX扩展复合IDPID-3 患者标识1~6+
XPN扩展人员姓名PID-5 姓名1~14
TS / DTM时间戳MSH-7 消息时间1~2
CE / CWE编码元素OBX-3 观察标识1~6
HD层级标识MSH-3 发送应用1~3

以 CE(编码元素)为例,其结构为"标识符^文本^编码系统^备用标识符^备用文本^备用编码系统"。当 OBX-3 写为 GLU^葡萄糖^L 时,表示检验项目代码为 GLU、名称为葡萄糖、编码系统为本地(L)。接口引擎在映射时,必须按数据类型逐组件取值,而不是把整个字段当成一个字符串。

四、消息类型与触发事件

MSH-9(消息类型)是路由的核心,它本身是一个复合字段,由三个组件构成:

  • 组件1 消息类型代码:如 ADT、ORM、ORU、SIU;
  • 组件2 触发事件代码:如 A01、A04、O01、R01;
  • 组件3 消息结构ID:如 ADT_A01、ORM_O01,v2.4 之后才普遍使用。

消息类型说明"这是什么业务",触发事件说明"为什么发这条消息"。例如 ADT^A01 是患者入院,ADT^A04 是患者注册,ADT^A08 是患者信息更新,三者消息结构相同但业务语义不同。接口引擎的路由规则通常写成"消息类型+触发事件"的组合键,例如:

路由规则示例:
  ADT^A01  →  入院登记服务  →  写入 patient_admission 表
  ADT^A08  →  信息更新服务  →  更新 patient_master 表
  ORU^R01  →  检验结果服务  →  解析 OBX 段写入 lab_result 表
  ORM^O01  →  医嘱服务      →  解析 ORC/OBR 段写入 order 表

五、转义与特殊字符处理

当数据本身含有分隔符时,必须用转义序列表示,否则解析会错位。HL7 v2 定义了标准转义序列:

转义序列含义示例
\F\字段分隔符 |地址中含 | 时写 \F\
\S\组件分隔符 ^姓名中含 ^ 时写 \S\
\T\子组件分隔符 &—
\R\重复分隔符 ~—
\E\转义符 \—
\X0D\十六进制 0D(回车)—
常见排障点:中文环境下,若发送方用 GBK 编码而接收方按 UTF-8 解析,会出现乱码;若发送方未对地址中的"|"做转义,接收方会把一个字段切成两个,导致后续所有字段整体错位。排查时应先打印原始字节流,确认 MSH-2 编码字符和字符集(MSH-18)是否与预期一致。

六、解析顺序与校验要点

接口引擎解析一条 HL7 v2 消息,建议按以下顺序进行,每一步都有明确的校验点:

  1. 读取 MSH-2,确定五个分隔符,后续所有切分都基于此;
  2. 按 \r 切分段,校验首段是否为 MSH,段名是否合法;
  3. 按 | 切分字段,注意 MSH 段字段编号偏移一位;
  4. 按 ^ 切分组件,按 & 切分子组件,按 ~ 处理重复;
  5. 按数据类型校验,检查必填组件是否存在、编码值是否在允许集合内;
  6. 按 MSH-9 路由,分发到对应业务处理器。