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 数据类型,数据类型规定了它有几个组件、每个组件什么含义。常见类型包括:
| 数据类型 | 含义 | 典型字段 | 组件数 |
|---|---|---|---|
| ST | 字符串 | PID-8 性别 | 1 |
| CX | 扩展复合ID | PID-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(回车) | — |
六、解析顺序与校验要点
接口引擎解析一条 HL7 v2 消息,建议按以下顺序进行,每一步都有明确的校验点:
- 读取 MSH-2,确定五个分隔符,后续所有切分都基于此;
- 按 \r 切分段,校验首段是否为 MSH,段名是否合法;
- 按 | 切分字段,注意 MSH 段字段编号偏移一位;
- 按 ^ 切分组件,按 & 切分子组件,按 ~ 处理重复;
- 按数据类型校验,检查必填组件是否存在、编码值是否在允许集合内;
- 按 MSH-9 路由,分发到对应业务处理器。