2. FHIR资源模型精解

FHIR 的资源模型不是“一张大表”,而是由一组可独立寻址、可独立版本化的资源单元组成。理解基础资源的关键,不是背字段名,而是抓住三条主线:字段语义决定“这个字段到底在说什么”,基数约束决定“它必须出现几次、能不能为空”,引用关系决定“资源之间如何连成临床事实”。Patient、Practitioner、Organization 是几乎所有交换场景都会出现的基础资源,它们之间的引用关系构成了后续 Encounter、Observation、MedicationRequest 等资源的骨架。

一、字段语义:从“标识”到“角色”的分层

以 Patient 为例,最容易被误用的字段是 identifier 与 id。资源实例的 id 是服务器内唯一的逻辑标识,用于 RESTful 寻址,例如 Patient/123;而 identifier 是业务标识,例如病案号、医保号、身份证号,它由业务系统分配,可能重复、可能变更,也可能一个患者拥有多个。把病案号写进 id 是常见错误,因为一旦合并患者或更换系统,id 不应承载业务含义。

Patient 的 name 是 HumanName 类型,包含 family、given、prefix、suffix 以及 use(official、usual、maiden 等)。中文场景下,family 放姓、given 放名,但要注意 text 字段可用于保留完整显示名,避免拆解歧义。gender 是必填的 administrative gender,取值为 male、female、other、unknown,它不是临床性别认同,后者应通过扩展表达。birthDate 是 date 类型,只到日,不包含时间;若需要精确到时分,应使用扩展或 Observation。

Practitioner 的语义核心是“医疗服务的提供者”,其 qualification 描述执业资质,包含 code、period、issuer。issuer 通常引用 Organization,表示发证机构。Organization 的 type 使用 CodeableConcept,常见取值来自 organization-type 值集,如 prov、dept、team、govt。partOf 是自引用,用于表达科室隶属于医院、医院隶属于医疗集团这类层级关系。

二、基数约束:0..1、0..*、1..1 的工程含义

FHIR 用基数表达“最少出现次数..最多出现次数”。1..1 表示必须且只能出现一次,例如 Patient 的 gender 在部分版本中为 0..1,但 resourceType 恒为 1..1。0..1 表示可选且最多一次,例如 birthDate。0..* 表示可重复,例如 identifier、name、telecom。1..* 表示至少一次,例如某些 Profile 中要求 Patient 至少有一个 identifier。

工程上,基数约束直接决定校验逻辑。若把 0..1 的字段当成数组处理,序列化时可能产生多余方括号;若把 0..* 的字段当成单值,则会在多标识场景下丢失数据。更隐蔽的是 0..1 与“空值”的区别:FHIR 中字段缺失表示“未知”,而字段存在但值为空字符串或空对象通常不被允许,应使用 dataAbsentReason 扩展表达“已知但无法提供”。

三、引用关系:从资源到资源的有向边

引用在 JSON 中表现为 Reference 对象,常见形式有三种:reference: "Patient/123" 是相对引用;reference: "http://example.org/fhir/Patient/123" 是绝对引用;identifier 加 type 是逻辑引用,用于跨系统未解析场景。引用方向决定了数据加载顺序:Observation.subject 指向 Patient,Encounter.subject 指向 Patient,Encounter.participant.individual 指向 Practitioner,Encounter.serviceProvider 指向 Organization。若先解析 Observation 再解析 Patient,就会出现悬空引用。

基础资源引用关系与字段语义速览 Patient id : 1..1(逻辑标识) identifier : 0..*(病案号/医保号) name : 0..*(HumanName) gender : 0..1(administrative) birthDate : 0..1(date) Practitioner identifier : 0..*(执业编号) name : 0..*(HumanName) qualification : 0..* ├ code : 1..1 └ issuer → Organization Organization identifier : 0..*(机构代码) type : 0..*(CodeableConcept) name : 0..1 partOf → Organization (自引用,表达层级) generalPractitioner qualification.issuer partOf(自引用) Encounter subject → Patient participant.individual → Practitioner serviceProvider → Organization subject participant serviceProvider 扩展机制 extension.url 唯一标识 value[x] 承载实际值 可嵌套、可重复 不破坏基础模型兼容性

四、扩展机制:不破坏互操作性的“安全阀”

当基础资源没有合适字段时,不应随意新增字段,而应使用 extension。每个扩展由 url 唯一标识,value[x] 承载实际值,可以是 string、CodeableConcept、Reference、boolean 等。扩展可以出现在资源根、某个元素内部,也可以嵌套。例如表达“患者临床性别认同”,可在 Patient 根下添加扩展,url 指向自定义 StructureDefinition,valueCodeableConcept 使用本地值集。扩展的代价是:接收方必须能识别该 url,否则只能保留但无法解释。因此工程上应优先使用已发布的 IG 扩展,自定义扩展必须配套 StructureDefinition 与值集。

五、JSON 与 XML 表示差异

同一资源在 JSON 与 XML 中语义等价,但表示形式不同。JSON 中数组用方括号,单值直接写;XML 中重复元素直接并列,单值也以元素形式出现。JSON 的 resourceType 是字符串字段,XML 中则体现为根元素名,如 <Patient>。扩展在 JSON 中是 extension 数组,在 XML 中是 <extension> 元素。引用在 JSON 中是对象,在 XML 中是 <reference value="Patient/123"/>。转换时最容易出错的是:JSON 中 0..1 字段被误写成数组,XML 中重复元素被误合并为单值。建议在映射层统一以“元素路径 + 基数”为中间模型,再分别序列化,避免直接 JSON 转 XML 造成结构漂移。

实践建议:在资源映射工程中,先为每个基础资源建立“字段—基数—类型—引用目标”四列表,再编写序列化适配器。这样无论输出 JSON 还是 XML,都能保证基数与引用方向一致,减少下游解析失败。