HL7
HL7的缩写Health Level Seven,HL7主要是用于医疗信息交换的一种交互格式。标准的HL7示例如下所示:
MSH|^~\&|HIS_InPatient|WinningSoft|TECH|WinningSoft|2022092929|pv1|ADT^A01|B763F986-920B-4D11-829F-0058F2A21818|P|2.4|||||CHN
EVN|A01|20220905092929
PID|1|5321^^^&PATID|5321^^^&PATID~0054749^^^&BLH~330102803012^^^&BRKH^2~3301241212021415^^^&SFZH~0^^^&YEXH||张三||192000000|M^^^^^^15867113807|||S^未知
NK1||张三|SEL^自己|||||||其他职业|19
PV1|1|I|4050^血液科^^^^^2928^血液科||||||||||1||||||68111|202~本地医保(住院)|||||||
PV2||||||||||||||||||||||||||||||||||||N
DG1|||D72.802^白细胞增多症|||0|0
段
什么是段?我理解类似与xml的节点头,每一段都会有一个父节点,如上面示例的MSH,EVN,PID,PV1,OBX,DG1,
不同类型的HL7消息包含不同的HL7段。
前面介绍HL7介绍了主要是用于医疗信息交换的格式,所有每一个段也代表了不同的含义,关于段名称的解释如下:
- MSH
包含有关消息本身的信息。该信息包括消息的发送者和接收者、消息的类型以及发送的日期和时间
- PID
包含患者基本信息,例如姓名、患者ID和地址等一些基本信息
- NK1
包含患者的亲属信息,如父/母的基本信息
- PV1
包含患者的就诊信息,如床号,病区,送检科室,送检医生等
- PV2
包含患者的患者就诊附加信息
- ORC
包含患者的患者医嘱信息,如医嘱项目,医嘱代码,相关的人员信息等
- OBR
包含患者的诊断以及观察的请求信息,用于记录医嘱信息
- OBX
包含患者观察的结果,如临床诊断,肉眼所见,电镜所见等
域
| HL7 的每一段都包含多个域,每个域用“ | ”来区分 |
域可以是原始数据类型(例如字符串或数字),也可以包含多个元素(Component)。如果某个域(fields)包含多个元素,则这些元素(Component)通常以^字符分隔。如果元素还包含子元素(Subcomponent),则这些子元素通常以&字符分隔,子元素(Subcomponent)必须 是原始数据类型(例如字符串或数字)。
-
” “分隔符中可以包含其他的分隔符: -
“^” 成分分隔符,表示该位置有多个属性,例如: 101761^张三 该位置是患者信息,101761是患者编号 张三是患者名字 - “~” 子成分分隔符,成分的下一分级
-
“&” 表示该位置是数组结构,类型相同可以循环 例如: &张三 &李四
消息类型
常用的消息类型可以分为以下几类,目前接口对接用到的最多的是ADT、ACK、ORM、ORU,列举这些主要是做一个备注
- ADT admit disCharge transfer 入院、出院、转院
- ACK acknowledgement message 应答消息
- BAR biling account record 账单账户记录
- DFT detailed financial transactions 详细的金融交易
- MDM Medical document management 医疗文件管理
- ORM order entry 订单录入
- ORU Observation result (unsolicited) 观察结果 非请求观察
- RDS pharmacy/treatment dispense 药房/治疗 配药
- RDE pharmacy/treatment encoded order 药房/治疗 编码顺序
- SIU schedlued information unsolicited 调度信息 非请求观察
XML的转换
目前在与其它信息系统交互的时候,因为涉及到HL7的读写,我使用的方法是通过一个封装类将HL7转换为XML,通过XML的节点来获取需要拿到的节点信息,代码片段如下所示:
public static XmlDocument ConvertToXmlObject(string sHL7)
{
_xmlDoc = CreateXmlDoc();
//hl7的处理,先把\r\n \n\r \r统一替换成\n,然后用\n来分隔行避免不同接口传过来的hl7换行方式不一致
sHL7 = sHL7.Replace("\r\n", "\n").Replace("\n\r", "\n").Replace("\r", "\n");
//把HL7分成段
string[] sHL7Lines = sHL7.Split('\n');//经过测试,TCP方式接收的用\r分隔,webService方式接收的用\n分隔
//遍历每一段
for (int i = 0; i < sHL7Lines.Length; i++)
{
// 判断是否空行
if (sHL7Lines[i] != string.Empty)
{
string sHL7Line = sHL7Lines[i];//某一段
//通过“|”分隔
string[] sFields = GetMessgeFields(sHL7Line);
// 为段(一行)创建第一级节点
XmlElement el = _xmlDoc.CreateElement(sFields[0]);
_xmlDoc.DocumentElement.AppendChild(el);
//遍历每个“|”与“|”间的内容
for (int a = 0; a < sFields.Length; a++)
{
// 为字段创建第二级节点
XmlElement fieldEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString());
//是否包括HL7的连接符
if (sFields[a] != @"^~\&")//0:如果这一行有任何分隔符,继续分隔。如果没有任何分隔符,可以直接写节点值。
{
//通过"~"分隔
string[] sComponents = GetRepetitions(sFields[a]);
if (1 == 1)//1:如果可以用“~”分隔,继续分隔;如果没有“~”符,开始用“~”分隔。不管有没有"~"符,都循环分隔
{
for (int b = 0; b < sComponents.Length; b++)
{
XmlElement componentEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString());
//通过"^"分隔
string[] subComponents = GetComponents(sComponents[b]);
if (subComponents.Length > 1)//2.如果有字组,大部分是没有的
{
for (int c = 0; c < subComponents.Length; c++)
{
//修改了一个错误
string[] subComponentRepetitions = GetSubComponents(subComponents[c]);
if (subComponentRepetitions.Length > 1)
{
for (int d = 0; d < subComponentRepetitions.Length; d++)
{
XmlElement subComponentRepEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString() + "." + c.ToString() + "." + d.ToString());
subComponentRepEl.InnerText = subComponentRepetitions[d];
componentEl.AppendChild(subComponentRepEl);
}
}
else
{
XmlElement subComponentEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString() + "." + c.ToString());
subComponentEl.InnerText = subComponents[c];
componentEl.AppendChild(subComponentEl);
}
}
fieldEl.AppendChild(componentEl);
}
else //2.如果没有字组了,大部分是没有
{
string[] sRepetitions = GetSubComponents(sComponents[b]);
if (sRepetitions.Length > 1)
{
XmlElement repetitionEl = null;
for (int c = 0; c < sRepetitions.Length; c++)
{
repetitionEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString() + "." + c.ToString());
repetitionEl.InnerText = sRepetitions[c];
componentEl.AppendChild(repetitionEl);
}
fieldEl.AppendChild(componentEl);
el.AppendChild(fieldEl);
}
else
{
componentEl.InnerText = sComponents[b];
fieldEl.AppendChild(componentEl);
el.AppendChild(fieldEl);
}
}
}
el.AppendChild(fieldEl);
}
}
else
{
//0:如果不可以分隔,可以直接写节点值了。
fieldEl.InnerText = sFields[a];
el.AppendChild(fieldEl);
}
}
}
}
return _xmlDoc;
}
/// <summary>
/// 通过|分隔
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private static string[] GetMessgeFields(string s)
{
return s.Split('|');
}
/// <summary>
/// 通过^分隔
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private static string[] GetComponents(string s)
{
return s.Split('^');
}
/// <summary>
/// 通过某连接符分隔
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private static string[] GetSubComponents(string s)
{
return s.Split('&');
}
/// <summary>
/// 通过~分隔 重复
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private static string[] GetRepetitions(string s)
{
return s.Split('~');
}
/// <summary>
/// 创建XML对象
/// </summary>
/// <returns></returns>
private static XmlDocument CreateXmlDoc()
{
XmlDocument output = new XmlDocument();
XmlElement rootNode = output.CreateElement("HL7Message");
output.AppendChild(rootNode);
return output;
}
| 大概逻辑就是段来设置父节点,根据关键字符(^ ~ | )来设置字段,这样可以直接通过指定的节点来获取所需要的信息,代码片段如下: |
log.Info("接收到的xml 信息为" + xml);
XmlDocument objDoc = new XmlDocument();
objDoc.LoadXml(xml);
XmlNode rootNode = objDoc.DocumentElement;
SynApplyInfo Syn = new SynApplyInfo();
//患者ID
Syn.PatientID = rootNode.SelectSingleNode("/HL7Message/PID/PID.2/PID.2.0/PID.2.0.0").InnerText;
//病历号
Syn.HisID = rootNode.SelectSingleNode("/HL7Message/PV1/PV1.19/PV1.19.0").InnerText;
//身份证号
Syn.IdentityCard = rootNode.SelectSingleNode("/HL7Message/PID/PID.3/PID.3.3/PID.3.3.0").InnerText;
//姓名
Syn.PatientName = rootNode.SelectSingleNode("/HL7Message/PID/PID.5/PID.5.0").InnerText;
