使用 Saxon-CE 和 HL7 CDA 实现客户端医疗保健应用程序

在浏览器中使用 XSLT 2.0 增强临床文档的视图

在 2011 XML Prague 会议上,Saxon XSL/XQuery 分析器的首席开发人员 Michael Kay 博士揭开了 Saxon-CE 的神秘面纱,这是一款通过 JavaScript 在 Web 浏览器中运行的客户端 XSLT 2.0 分析器。本文将介绍如何结合使用 XSLT 2.0 和 Saxon-CE 为一个简单医疗保健应用程序构建应用程序视图,该应用程序从一个使用 Health Level 7 Clinical Document Architecture (HL7 CDA) 编写的临床文档运行。

Piers Michael Hollott, 高级顾问, Sierra Systems

Piers Hollott 从事软件行业工作已经 15 年了,他专长于 Java 开发、XML 技术和函数式编程。他为好几个开源项目贡献过力量,目前是 Sierra Systems 的一名顾问。



2011 年 11 月 21 日

常用缩略词

  • CSS:层叠样式表
  • DOM:文档对象模型
  • HTML:超文本标记语言
  • URL:统一资源定位符
  • XHTML:可扩展 HTML
  • XML:可扩展标记语言
  • XSL:可扩展样式表语言
  • XSLT:可扩展样式表语言转换

Health Level 7 (HL7) Clinical Document Architecture (CDA) 基于 HL7 version 3 参考信息模型 (RIM),该模型用于开发 HL7 v3 消息传递。HL7 消息传递表示个人医疗保健交互(比如查询、响应和通知),而 HL7 CDA 文档则是单个持久化文档,用于表示患者就诊、持续监护 (continuity-of-care) 等类似文档。为了便于人们阅读 CDA 中的信息,每个 CDA 文档除了包括机器可读的内容外,还要有一个文本描述,如 清单 1 所示。严格说来,HL7 v3 消息旨在通过电缆传输(例如,作为 “客户端对服务器” 交互或 “服务器对服务器” 数据分发的一部分);由于它们也用来供人类阅读,所以还可以通过其他方式(比如电子邮件)来传输 HL7 CDA 文档。

清单 1. CDA 中的一条观察条目:Penicillin 过敏症状
<component>
    <section>
        <code code="10155-0" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
        <title>Allergies and Adverse Reactions</title>
        <text>
            <list>
                <item>Penicillin - Hives</item>
            </list>
        </text>
        <entry>
            <observation classCode="OBS" moodCode="EVN">
                <code xsi:type="CD" code="247472004" codeSystem="2.16.840.1.113883.6.96" 
                    codeSystemName="SNOMED CT" displayName="Hives"/>
                <statusCode code="completed"/>
                <entryRelationship typeCode="MFST">
                    <observation classCode="OBS" moodCode="EVN">
                        <code xsi:type="CD" code="91936005" 
                            codeSystem="2.16.840.1.113883.6.96" 
                            codeSystemName="SNOMED CT" 
                            displayName="Allergy to penicillin"/>
                        <statusCode code="completed"/>
                    </observation>
                </entryRelationship>
            </observation>
        </entry>
    </section>
</component>

Saxon 分析器提供在服务器端上运行 XSLT 2.0 转换的支持已经有许多年了。在最近的 2011 XML Prague 会议上,Saxon 分析器的原创者 Michael Kay 博士介绍了 Saxon Client Edition (Saxon-CE) 的 alpha 版本,该版本支持在 Web 客户端应用程序(在浏览器中)中直接运行 XSLT 2.0 转换。使用 JavaScript 在任意现代浏览器上运行 Saxon-CE,这一直都是关于客户端 XSL 的研究问题:历来各种浏览器都没有统一地实现 XSL 支持。由于 Saxon-CE 采用了 JavaScript 作为运行时引擎,因此解决了此问题。

另外,XSLT 2.0 提供了几个原先 XSLT 1.0 规范所不支持的特性,比如支持多个文档和更强的数据类型设置。Saxon-CE 问世后,这些特性现在在客户端中均受到支持,如本文所示,其中有些特征非常适合用于客户端应用程序视图,特别是多个文档的客户端支持。

开始

在开发本文描述的 CDA Viewer 应用程序,我使用了 Unite Web 服务器,它是 Opera 最新版本所附带的服务器。您也可以使用您喜欢的任何服务器。由于本文讨论的是如何开发一个客户端应用程序视图,所以不再介绍如何持久化或分发 CDA 文档。在应用程序的测试方面,我直接通过 Web 服务器将一个样例 CDA 发布为一个 URL(或者,您也可以将您的 CDA 文档持久化到 IBM® DB2® pureXML 之类的 XML 数据库中)。同样的,我也将 XSL 转换发布为 URL。

下载 Saxon-CE

Saxon-CE alpha 版本目前可以从 Saxonica 网站上下载(参见 参考资料 中的链接)。目前,该 alpha 版本还不能用于生产应用程序;但是,基于原先 Saxon 分析器的成功,我确信,Client Edition 将很快成为行业标准,尤其是如果它能赢得来自早期使用者的积极反馈的话。

这个 alpha 版本附带了丰富的发布说明。您可以将解压的发布文件夹中的 JavaScript 文件以及其他任何 .js 脚本复制到您的应用程序服务器。您还应该花点时间下载样例应用程序。该 JavaScript 包含了一个 .nocache.js 文件和几个缩小的 .js 文件,每个文件针对于每个主要浏览器。这些文件包含实际的分析器代码;.nocache.js 文件先检测使用的是哪个浏览器,然后再调用相应的 .js 脚本。样例应用程序包含一个 XSL 转换文件、一些 XML 数据以及一个 HTML 文件,该文件会调用 Saxon-CE JavaScript 脚本来运行转换。我构建的 CDA Viewer 应用程序遵循相同的模式,但是为了可读性,我将转换层拆分为三个文件(参见 下载 部分)。在 Web 服务器上,我为每个样例应用程序各安排一个文件夹,还为 CDA 应用程序安排了一个文件夹。


探索 XSLT 2.0 中的特性

XSLT 2.0 规范引入了很多特性,比如函数定义、多结果文档、序列和临时树、更强的类型设置以及文本分析。本文主要关注多结果文档支持。在服务端,使用多结果文档意味着在服务器上创建多个文档,这样就能创建一个 XHTML 主页来引用服务器上生成的其他几个结果页。在服务端转换上,使用多结果文档仅仅意味着:多个文档。清单 2 展示了这种解决方法。使用 Saxon-CE 在客户端上生成多个文档时,生成的内容实际上指向您的客户端应用程序的各个屏幕区域(通常是 HTML 页中的 div 元素)。

清单 2. (服务器上的)多结果文档
<xsl:for-each select="section">
    <xsl:result-document href="{@id}.html">
        <xsl:apply-templates select="." mode="html" />
    </xsl:result-document>
</xsl:for-each>

转换临床文档

我将 XSL 转换分为三个文件(参见 下载 部分):

  • cda.xsl:包含基本转换,用于显示人类可读文档。
  • cda-machine-readable.xsl:包含一个可定制层,目前可以一种更整洁的方式显示机器可读的文档细节。
  • cda-dom-interaction.xsl:包含一个模板,用于处理与 HTML 页的 DOM 之间的交互,比如单击事件。

另外,本身还有一个 CSS 文件和 HTML 页。

CSS 比较简单。HTML 页也比较简单,只包含几个用于 CDA Viewer 中各屏幕区域的 div 元素,每个元素都可以通过一个 ID 在 CSS 中定位。我的计划是加载并转换整个 CDA,然后允许通过 DOM 进行交互式访问,这样我就能拥有一个名为 div-cachediv 元素,我将该元素隐藏在 CDA Viewer 底部。这个缓存包含 CDA 中所记录的每个条目的副本。您可以向下滚动查看文本,也可以将这个元素标记为 style="visibility:hidden;inline:none",使其隐藏起来。

清单 3 中的代码用于触发转换过程。如您所见,<script> 标记首先会调用 Saxon-CE JavaScript,然后再调用 XSL 转换样式表,以 CDA 文档为目标。

清单 3. 从 HTML 页调用 Saxon-CE
<script type="text/javascript" language="javascript" src="../Saxonce.nocache.js"></script>
<script type="application/xslt+xml" language="xslt2.0" src="cda.xsl" 
                      input="SampleCDADocument.xml"></script>

显示 CDA Viewer 的目录

清单 3 中,主转换首先从 CDA 包装器中提取一些信息,为 CDA Viewer 创建一个简明标题。而且,您能够看到 xsl:result-document 的运行效果。在 清单 4 中,注意一下 <div> 标记是如何通过其 #creation ID 标识的。method="ixsl:replace-content" 是 “in the interactive XSL, replace the content” 的简写。稍后您将看到一个附加内容的示例。注意,当您直接处理 CDA 时,只要一引用 CDA 中的元素,都需要使用 hl7: 命名空间。稍后您将看到,在处理引用 DOM 的模板时,并不需要这样做。这个过程可能比较麻烦,这也是我将模板拆分为几个 XSL 样式表的原因之一。

清单 4. 使用 xsl:result-document 替换内容
<xsl:result-document href="#creation" method="ixsl:replace-content">
    <div>
        <xsl:value-of>
            <xsl:value-of select="hl7:ClinicalDocument/hl7:title"/>: 
            <xsl:value-of 
              select="hl7:ClinicalDocument/hl7:recordTarget/hl7:patientRole/hl7:patient"/>
        </xsl:value-of>
    </div>
</xsl:result-document>

在同一个 XSL 文件中的下一个代码段(参见 清单 5)中,您将看到如何从 CDA 文档中的各个部分生成 CDA Viewer 目录。请您查看一些关于目录格式化详细信息的样例文件。我们采用了一种类似的方法来缓存来自 CDA 的临床输入数据。在这两个案例中,您可以了解到,在每次应用模板时结果是如何被附加到目标 <div> 元素的现有内容的。

清单 5. 使用 xsl:result-document 附加内容
<xsl:result-document href="#notes" method="ixsl:append-content">
    <xsl:apply-templates select="//hl7:component/hl7:section" mode="notes"/>
</xsl:result-document>

<xsl:result-document href="#div-cache" method="ixsl:append-content">
    <xsl:apply-templates select="//hl7:component/hl7:section" mode="cache"/>
</xsl:result-document>

图 1 展示了基本 CDA Viewer 和目录。

图 1. CDA Viewer 目录
屏幕截图:CDA View 应用程序的目录

与 DOM 交互

所生成的 ID 用于将目录中的链接与缓存中的 CDA 条目相连接起来。清单 6 提供了用于在目录中创建条目的模板的一个节选。

清单 6. 链接到缓存的 CDA 条目
<a class="notes-section">
    <xsl:attribute name="id"><xsl:value-of select="generate-id(.)"/></xsl:attribute>
    <xsl:value-of select="hl7:title"/>
</a>

清单 7 中,注意下如何使用模板上的 mode="ixsl:onclick" 属性来告知 Saxon-CE 应用这个模板来处理单击事件。这与其他的 JavaScript 单击事件相似:仅通过 Saxon-CE 和 XSL 样式表处理。注意,只要分析器处理这类单击事件,上下文就会从原始 XML 文档转移到 HTML 页的 DOM,这也是我将这些模板单独放置到一个名为 cda-dom-interaction.xsl 的转换样式表中的原因。模板本身显示 CDA 条目的文本, 该条目是从此前用原始单击事件相关联 ID(原始锚链接的 ID)所创建的缓存中提取的。这个文本替换 note-detail 结果文档 div element 的内容。然后模板会应用任何适用的下游模板来处理 CDA 条目中的机器可读内容。

清单 8. 处理来自目录的单击事件
<xsl:template match="a[@class='notes-section']" mode="ixsl:onclick">
    <xsl:variable name="div-id" select="@id"/>
    <xsl:variable name="selected-section" 
        select="//div[@id='div-cache']/div[@id=$div-id]/section"/>

    <xsl:result-document href="#note-detail" method="ixsl:replace-content">
        <h2><xsl:value-of select="$selected-section/title/text()"/> 
            (<xsl:value-of select="$div-id"/>)</h2>
        <div><xsl:value-of select="$selected-section/text"/></div>
        <h2>Machine-readable Content</h2>
        <xsl:apply-templates select="$selected-section" mode="mach-read">
            <xsl:with-param name="a" select="."/>
        </xsl:apply-templates>
    </xsl:result-document>
</xsl:template>

为了隔离机器可读内容和人类可读内容,我创建并包含了一个第三个转换样式表 cda-machine-readable.xsl。隔离这些模板有这样一个好处:可以对第三个样式表进行扩展或完全替换,以便使用一个更复杂的样式表来处理 CDA 中的机器可读信息。例如,您可能想提供更美观的表或数据图形表示,或者保持某些条目类型。或者,您可能想深入一步,加载另一个 XML 文档(也许是从一个元数据存储库加载),扩展机器可读数据中的一些代码,或者使用常用代码系统对它们进行归类。保留模板来转换机器可读条目是一种良好编程实践,正如通常分隔关注问题那样。当我处理大规模转换或我期望执行的转换时,我发现这种实践非常关键。

显示机器可读临床条目

我的最小机器可读转换样式表只包含单个模板,该模板标识若干已知条目类型(Observation、Substance Administration、Procedure 和 Act 事件)并使用项目列表和表样式化它们。我并不打算在这里做一些太花哨的事情,但查看一个简单模板如何通过样式和组织提供丰厚的回报是一个有用的练习。还要注意下,在 清单 8 中,起源锚链接是如何通过一个参数被传递给模板。这个步骤对于显示机器可读数据不是必要的。恰恰相反,链接要通过这个模板传递,并缓存在条目细节底部的一个隐藏的 div 元素中以便将来使用。这个链接信息稍后将用于构建我所说的别针区域 (pin area)。为简洁起见,我省去了所有内容,只保留了针对 xsl:choose 中的 Observation 的 xsl:when

清单 8. 模板化机器可读的 CDA 的条目
<xsl:template match="section" mode="mach-read">
    <xsl:param name="a"/>

    <div id="mach-read">
        <xsl:choose>
            <xsl:when test="entry/observation">
                <h3>Observations</h3>
                <ul>
                    <xsl:for-each select="entry/observation">
                        <li><a>
                            <xsl:value-of select="text"/>
                            <xsl:value-of select="code/@displayName"/>
                            <xsl:for-each select="value">
                            (<xsl:value-of><xsl:value-of select="@value"/> 
                                <xsl:value-of select="@unit"/></xsl:value-of>)
                            </xsl:for-each>
                        </a></li>
                    </xsl:for-each>			
                </ul>
            </xsl:when>
            <xsl:otherwise>(no match)</xsl:otherwise>
        </xsl:choose>
        <div class="xref" style="visibility:hidden;inline:none">
            <xsl:copy-of select="$a"/>
        </div>
    </div>
</xsl:template>

注意,由于这个模板应用于缓存在 DOM 中的 CDA 数据副本,hl7: 不是 entrycodeobservation 等元素所必需的。您也可以将这个命名空间添加到缓存的 CDA 条目,但这样做没有多少好处,因为不会进一步执行序列化。如果页面上的 HL7 CDA 元素和其他元素之间存在混淆,这样做具有实际意义。

图 2 展示了一个 CDA Viewer,其中包含了一个表示过敏和不良反应的观察事件。

图 2. 诸如过敏和不良反应的观察事件
显示过敏和不良反应等观察事件的 CDA Viewer 的屏幕截图

开发一种新设计模式:别针区域

别针区域是在开发转换过程中出现的一种设计模式,它是受到 Saxon-CE 实现 XSLT 2.0 结果文档的方法所启发的。如前所述,生成的内容可以附加到一个屏幕区域,也可以替换现有内容。通常,您需要替换现有内容;但是,当您重复应用一个模板时,就可能需要附加内容。如果您将几个模板应用到同一个目标屏幕区域,那么可以创建一个类似于书签的集合:您可以以交互式方式逐渐添加这个集合,而不必移除屏幕区域中的任何现有内容。

清单 9 中的样例代码中,查看模板以固定锚链接。没错,这是一个样例应用程序,但这里的理念可以向您提供启发。当您单击这个 Viewer 的目录中的链接时,这个链接事件的处理方法是显示 CDA 的那个部分的内容。但是,当您单击其他链接时,一个简单的锚模板将处理链接事件并将链接的内容复制到别针区域。结果是您可以使用 Viewer 来查看临床文档的内容并 “固定” 一些条目以便将来参考。

清单 9. 模板化别针区域
<xsl:template match="a" mode="ixsl:onclick">
    <xsl:variable name="div-mach-read" select="ancestor::div[@id='mach-read']"/>
    <xsl:result-document href="#pin" method="ixsl:append-content">
        <div>
            <xsl:attribute name="title"><xsl:value-of select="$div-mach-read/h3"/> 
            - <xsl:value-of select="text()"/></xsl:attribute>
            <xsl:copy-of select="$div-mach-read/div[@class='xref']/a"/> -
            <xsl:value-of select="text()"/>
        </div>
    </xsl:result-document>
</xsl:template>

图 3 展示了别针区域的应用情况。

图 3. CDA Viewer 的别针区域
CDA Viewer 别针区域以及其中提到的项的屏幕截图

当然,如果您能使用固定的条目来做一些有用的事,那么这种设计模式会带来更多的好处;但是,关于这方面的讨论已超出了本文的范围。一个可能的使用案例可能要包含一个电子邮件浏览器插件,该插件可允许患者查看来自其医生的 CDA,固定一些条目,然后将它们包含到一个电子邮件响应中。由于它们处理信息的声明性方式,毫无疑问,Saxon-CE 和 XSLT 2.0 将是开发这些应用程序的有用工具。


结束语

CDA 文档是持久性文档,因此通常相当大。尽管旨在供人类阅读,但 CDA 文档中仍然包含了大量的机器可读信息,可以通过将这些信息转换为一个扩展的客户端视图使其易于管理。我敢肯定,随着未来版本的发布,Saxon-CE 将成为实现上述目标的关键工具,原因有两个:它能在任何现代浏览器上运行;它能提供一些客户端 XSLT 2.0 特性,比如多结果文档支持。我完全相信这种技术将成为富医疗保健应用程序开发人员必不可少的技术。


下载

描述名字大小
样例 CDA 应用程序cda_sample.zip16KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=775904
ArticleTitle=使用 Saxon-CE 和 HL7 CDA 实现客户端医疗保健应用程序
publish-date=11212011