Manipulating FHIR Resource: HAPI FHIR API

SIL-Thailand
SIL-Thailand
Published in
4 min readApr 17, 2020

จัดการ FHIR Resource ผ่าน Java Programming API: HAPI FHIR

ในตอนนี้จะเป็นการจัดการ FHIR resource ด้วย programming API ใน Java platform

HAPI

HAPI (HL7 API) project เป็น open-source implementation ของ HL7 specification ใน Java platform ตั้งแต่ HL7 ยังเป็นรุ่น 2.x เริ่มต้นโดย University Health Network (UHN) (https://www.uhn.ca/) ซึ่งเป็นกลุ่มเครือข่ายโรงพยาบาลขนาดใหญ่หลายแห่งที่มีการสอนบุคลากรทางการแพทย์ในระดับคลินิก (teaching hospital) ในเมือง Toronto, ประเทศ Canada

James Agnew

โดยมี James Agnew ซึ่งเป็น Lead Architect ของ Centre for Global eHealth Innovation ของ UHN และหนึ่งในสมาชิกของ FHIR Core Team เป็น project leader ของ HAPI project

James Agnew at Regional FHIR Workshop, ADB HQ, Manila, Philippines
New HAPI FHIR mascot

HAPI FHIR (https://hapifhir.io) เป็น library ที่เป็น open-source implementation ของมาตรฐาน HL7 FHIR บน Java platform

HAPI FHIR รุ่นล่าสุดคือรุ่น 4.2.0 สนับสนุน JDK ตั้งแต่รุ่น JDK8 ขึ้นมา และสนับสนุน FHIR ตั้งแต่รุ่น DSTU2, DSTU3, R4 (HL7 FHIR รุ่น DSTU3 เปลี่ยนชื่อเป็น STU3 ในเวลาต่อมา แต่ใน HAPI ยังใช้ DSTU3 อยู่ตามเดิม เพื่อให้ code เดิมยังใช้งานต่อได้)

HAPI FHIR มี class library สำหรับ resource และ data type ทุกประเภทที่กำหนดโดยข้อกำหนด FHIR ตัวอย่างเช่น class สำหรับ Patient resource หากดู JavaDoc จะเห็น getters และ setters สำหรับ properties ต่างๆ ที่ประกอบขึ้นเป็น Patient resource

FHIR Context

ในการใช้งาน object ต่างๆ ที่สร้างขึ้นจาก class library นี้ จำเป็นต้องสร้าง FhirContext ขึ้นมาก่อน (JavaDoc) FhirContext เป็นจุดเริ่มต้นสำหรับการใช้งาน HAPI และขึ้นอยู่กับรุ่นของ FHIR ที่ต้องการใช้งานในแอป

// Create a context for DSTU3
FhirContext ctx = FhirContext.forDstu3();
// or create a context for R4
FhirContext ctx = FhirContext.forR4();

FHIRContext เป็น object ที่ใช้ทรัพยากร จึงควรสร้าง object ไว้ครั้งเดียวและเก็บไว้ใช้งานตลอดทั้งแอปจนกว่าจะปิดแอป

HAPI FHIR Modules

Modules ที่สำคัญใน HAPI FHIR library สำหรับการใช้งาน ได้แก่

  • Core Library: hapi-fhir-base เป็น core HAPI FHIR library จำเป็นเสมอสำหรับการใช้งาน framework (JavaDoc)
  • Structure: เป็น library ของ model classes ต่างๆ จาก FHIR resources และ data types ขึ้นอยู่กับรุ่นของ FHIR ที่ต้องการสนับสนุน สำหรับรุ่น R4 คือ hapi-fhir-structures-r4 (JavaDoc)
  • Client Framework: hapi-fhir-client เป็น library ของ core FHIR client framework จำเป็นสำหรับการใช้งานฟังก์ชั่นต่างๆ ของ client ใน HAPI (JavaDoc)
  • Validation: เป็น library ของ FHIR Profile Validator ใช้เพื่อตรวจสอบความถูกต้องของข้อมูล Resources กับ FHIR Profiles
  • Server: สำหรับใช้พัฒนา FHIR-compliant server กับ data storage ที่มีอยู่

รายละเอียดของแต่ละ modules ดูได้จากที่นี่

Importing HAPI FHIR

วิธีที่ง่ายที่สุดในการใช้ HAPI FHIR คือการใช้ระบบ build system ที่จัดการ dependency โดยอัตโนมัติ ระบบจะช่วยดาวน์โหลดและเพิ่ม library ที่ต้องการใน classpath ในที่นี้จะใช้ระบบของ Apache Maven

ใน Maven project file (pom.xml) ต้องมี HAPI FHIR core JAR และ Structure JAR (อย่างน้อย 1 รุ่นที่ต้องการ)

<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>4.2.0</version>
</dependency>

หากต้องการใช้งานฟังก์ชั่นของ client ใน HAPI เพิ่ม module client รวมเข้าไป

<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>4.2.0</version>
</dependency>

ต่อไปจะอ่านข้อมูลเดิมที่เคยสร้างไว้ด้วย clinFHIR และเก็บไว้ที่ Public test server ของ HAPI (http://hapi.fhir.org/baseR4)

Reading FHIR Resource with HAPI

สร้าง FHIR Context และ Generic Client สำหรับอ่านข้อมูล resource จาก server ที่เก็บข้อมูล resource

FhirContext ctx = FhirContext.forR4();                                                  IGenericClient client = ctx.newRestfulGenericClient("http://hapi.fhir.org/baseR4");

Patient Resource

อ่านข้อมูล Patient resource, Id 921009 เก็บไว้ใน Patient object สำหรับใช้งาน

Patient patient = client.read().resource(Patient.class).withId("921009").execute();

แสดงผลข้อมูล Patient resource ใน data element ที่ต้องการ

System.out.printf("NAME: %s%n", patient.getNameFirstRep().getNameAsSingleString());System.out.printf("BIRTHDATE: %tF%n", patient.getBirthDate());

โมเดลข้อมูล FHIR นั้นสมบูรณ์เพียงพอที่จะรองรับกรณีใช้งานทั่วไป แต่บางครั้งความสมบูรณ์นั้นเพิ่มความซับซ้อน ตัวอย่างเช่น ผู้ป่วยอาจมีได้หลายชื่อ (ชื่อที่เป็นทางการ ชื่อเล่น ฯลฯ ) element name ใน Patient resource จึงมี cardinality เป็น 0..* ของ data type HumanName หากใช้ method getName() (JavaDoc) ใน HAPI FHIR จะส่งค่ากลับมาเป็น List<HumanName> แต่ HAPI FHIR มี method ที่ช่วยอำนวยความสะดวกในการส่งค่า HumanName แรกใน element name คือ getNameFirstRep() (JavaDoc)

Condition Resource

แสดง Problem list จาก list ของ object Condition ที่มี subject=921009

Bundle conditionBundle = client.search().forResource(Condition.class)
.where(Condition.SUBJECT.hasId("921009"))
.returnBundle(Bundle.class).execute();
if(conditionBundle.hasEntry()) {
System.out.println("PROBLEM LIST:");
List<BundleEntryComponent> entries = conditionBundle.getEntry(); int i = 1;
for (BundleEntryComponent ent : entries) {
Condition c = (Condition)ent.getResource();
System.out.printf("%3d. %-40s %-12s %n",
i++,
c.getCode().getText(),
c.getSeverity().getText());
}
}

Method execute() จะส่งค่ากลับมาเป็น object ชนิดเดียวกับ object ที่เรียก method นี้ ในตัวอย่างแรก withId() ถูกเรียกจาก method resource(Patient.class) เมื่อเรียก method execute() ต่อจึงได้ค่ากลับมาเป็น Patient object ส่วนในตัวอย่างที่สอง returnBundle() ถูกเรียกจาก method forResource(Condition.class).where(…) เมื่อเรียก method execute() ต่อจึงได้ค่ากลับมาเป็น Bundle object ที่สามารถ cast entry ใน Bundle ไปเป็น Condition object ได้

(JavaDoc: class Condition, method execute(), withId(), resource(), forResource(), returnBundle() )

Output:

Encounter Resource

ในประเทศไทยอาจจะคุ้นเคยกับคำว่า “visit” สำหรับเรียกการมารับบริการในแต่ละครั้ง แต่ HL7 จะเรียกตรงนี้ว่า “encounter” (https://www.hl7.org/fhir/encounter.html)

ค้นหา Encounter resource ที่มี subject=921009

Bundle encounterBundle = client.search().forResource(Encounter.class)
.where(Encounter.SUBJECT.hasId("921009"))
.returnBundle(Bundle.class).execute();
if(encounterBundle.hasEntry()) {
System.out.println("ENCOUNTER LIST:");
List<BundleEntryComponent> entries = encounterBundle.getEntry(); for (BundleEntryComponent ent : entries) {
Encounter e = (Encounter)ent.getResource();
System.out.printf("DATE: %tF TYPE: %-15s PARTICIPANT: %s %n",
e.getPeriod().getStart(),
e.getTypeFirstRep().getText(),
e.getParticipantFirstRep().getIndividual().getDisplay());
}
}

(JavaDoc: class Encounter)

Output:

Full code listing:

หมายเหตุ:

  • Java ที่ใช้ demo เป็นรุ่น Java 11 ซึ่งอาจมี syntax หรือ method ที่ไม่เหมือนกับ Java 8

--

--