Thư viện hapi-fhir-validation là một thành phần quan trọng trong hệ sinh thái HAPI FHIR, cung cấp các cơ chế toàn diện để kiểm tra tính hợp lệ (validation) của FHIR resources. Validation là một quá trình thiết yếu trong các ứng dụng y tế, đảm bảo rằng dữ liệu tuân thủ theo đặc tả FHIR và các ràng buộc bổ sung được định nghĩa trong profiles.
Thư viện này được xây dựng trên nền tảng hapi-fhir-base và hoạt động với tất cả các phiên bản FHIR (DSTU2, DSTU3, R4, R5), cung cấp các API mạnh mẽ để thực hiện validation ở nhiều cấp độ khác nhau, từ kiểm tra cấu trúc cơ bản đến validation phức tạp dựa trên terminologies và profiles.
Đặc điểm chính
1. Validation dựa trên StructureDefinition profiles
FHIR Profiles (StructureDefinitions) cho phép định nghĩa các ràng buộc và mở rộng đối với các FHIR resources. Thư viện hapi-fhir-validation cung cấp khả năng validation resources dựa trên các profiles này:
Kiểm tra tuân thủ với các ràng buộc cardinality (số lượng phần tử tối thiểu/tối đa)
Kiểm tra các ràng buộc về kiểu dữ liệu
Kiểm tra các invariants (ràng buộc phức tạp thông qua FHIRPath)
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.Patient;
public class BasicValidationExample {
public static void main(String[] args) {
// Khởi tạo FhirContext
FhirContext ctx = FhirContext.forR5();
// Tạo validator
FhirValidator validator = ctx.newValidator();
// Tạo resource hợp lệ
Patient patient = new Patient();
patient.addName().setFamily("Nguyễn").addGiven("Văn A");
// Thực hiện validation
ValidationResult result = validator.validateWithResult(patient);
// Kiểm tra kết quả
System.out.println("Validation successful: " + result.isSuccessful());
System.out.println("Issues found: " + result.getMessages().size());
// In ra chi tiết các vấn đề
result.getMessages().forEach(message -> {
System.out.println("Location: " + message.getLocationString());
System.out.println("Severity: " + message.getSeverity());
System.out.println("Message: " + message.getMessage());
System.out.println();
});
}
}
Ví dụ 2: Sử dụng FhirInstanceValidator
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.Enumerations;
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
public class InstanceValidatorExample {
public static void main(String[] args) {
// Khởi tạo FhirContext
FhirContext ctx = FhirContext.forR5();
// Tạo validator
FhirValidator validator = ctx.newValidator();
// Thêm instance validator
validator.registerValidatorModule(new FhirInstanceValidator(ctx));
// Tạo patient không hợp lệ (thiếu giá trị bắt buộc)
Patient invalidPatient = new Patient();
invalidPatient.setGender(Enumerations.AdministrativeGender.OTHER);
// Không thêm name (bắt buộc với nhiều profile)
// Thực hiện validation
ValidationResult result = validator.validateWithResult(invalidPatient);
// Kiểm tra kết quả
System.out.println("Validation successful: " + result.isSuccessful());
System.out.println("Issues found: " + result.getMessages().size());
// In ra chi tiết các vấn đề
result.getMessages().forEach(message -> {
System.out.println("Location: " + message.getLocationString());
System.out.println("Severity: " + message.getSeverity());
System.out.println("Message: " + message.getMessage());
System.out.println();
});
}
}
Ví dụ 3: Validation dựa trên profile
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.common.hapi.validation.support.*;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r5.model.*;
public class ProfileValidationExample {
public static void main(String[] args) {
// Khởi tạo FhirContext
FhirContext ctx = FhirContext.forR5();
// Tạo ValidationSupport
// 1. Default validation (mặc định từ FHIR)
DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport(ctx);
// 2. Nạp tài nguyên từ JAR
PrePopulatedValidationSupport prePopulatedSupport = new PrePopulatedValidationSupport(ctx);
// 3. Nạp InMemoryTerminologyServer
InMemoryTerminologyServerValidationSupport terminologySupport = new InMemoryTerminologyServerValidationSupport(ctx);
// 4. Repository cho các tài nguyên validation
SnapshotGeneratingValidationSupport snapshotSupport = new SnapshotGeneratingValidationSupport(ctx);
// 5. Kết hợp các validation support
ValidationSupportChain validationChain = new ValidationSupportChain(
defaultSupport,
prePopulatedSupport,
terminologySupport,
snapshotSupport);
// Tạo FhirInstanceValidator
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ctx);
instanceValidator.setValidationSupport(validationChain);
// Tạo validator và đăng ký module
FhirValidator validator = ctx.newValidator();
validator.registerValidatorModule(instanceValidator);
// Tạo Patient với profile
Patient patient = new Patient();
patient.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/Patient");
patient.addName().setFamily("Nguyễn").addGiven("Văn A");
patient.setGender(Enumerations.AdministrativeGender.MALE);
// Thêm identifier không đúng với profile
Identifier identifier = patient.addIdentifier();
identifier.setSystem("invalid-system");
identifier.setValue("12345");
// Thực hiện validation
ValidationResult result = validator.validateWithResult(patient);
// Kiểm tra kết quả
System.out.println("Validation successful: " + result.isSuccessful());
System.out.println("Issues found: " + result.getMessages().size());
// In ra chi tiết các vấn đề
result.getMessages().forEach(message -> {
System.out.println("Location: " + message.getLocationString());
System.out.println("Severity: " + message.getSeverity());
System.out.println("Message: " + message.getMessage());
System.out.println();
});
}
}
Ví dụ 4: Validation terminology
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.common.hapi.validation.support.*;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r5.model.*;
public class TerminologyValidationExample {
public static void main(String[] args) {
// Khởi tạo FhirContext
FhirContext ctx = FhirContext.forR5();
// Thiết lập validation support chain với terminology server
ValidationSupportChain validationSupportChain = new ValidationSupportChain(
new DefaultProfileValidationSupport(ctx),
new InMemoryTerminologyServerValidationSupport(ctx),
new CommonCodeSystemsTerminologyService(ctx)
);
// Tạo instance validator với validation support
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ctx);
instanceValidator.setValidationSupport(validationSupportChain);
// Tạo validator và đăng ký module
FhirValidator validator = ctx.newValidator();
validator.registerValidatorModule(instanceValidator);
// Tạo Observation với coding hợp lệ
Observation observation = new Observation();
observation.setStatus(Observation.ObservationStatus.FINAL);
// Thêm code từ LOINC
CodeableConcept code = observation.getCode();
code.addCoding()
.setSystem("http://loinc.org")
.setCode("8867-4")
.setDisplay("Heart rate");
// Thêm category với coding hợp lệ
CodeableConcept category = observation.addCategory();
category.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/observation-category")
.setCode("vital-signs")
.setDisplay("Vital Signs");
// Thêm coding không hợp lệ để minh họa lỗi
CodeableConcept invalidCategory = observation.addCategory();
invalidCategory.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/observation-category")
.setCode("INVALID-CODE")
.setDisplay("Invalid Code");
// Thực hiện validation
ValidationResult result = validator.validateWithResult(observation);
// Kiểm tra kết quả
System.out.println("Validation successful: " + result.isSuccessful());
System.out.println("Issues found: " + result.getMessages().size());
// In ra chi tiết các vấn đề
result.getMessages().forEach(message -> {
System.out.println("Location: " + message.getLocationString());
System.out.println("Severity: " + message.getSeverity());
System.out.println("Message: " + message.getMessage());
System.out.println();
});
}
}
Ví dụ 5: Validation custom profile
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.common.hapi.validation.support.*;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r5.model.*;
import java.io.FileReader;
public class CustomProfileValidationExample {
public static void main(String[] args) throws Exception {
// Khởi tạo FhirContext
FhirContext ctx = FhirContext.forR5();
// Thiết lập các validation support
DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport(ctx);
// Support để lưu trữ profile tùy chỉnh
PrePopulatedValidationSupport prePopulatedSupport = new PrePopulatedValidationSupport(ctx);
// Nạp custom profile từ file JSON
String profileJson = // đọc từ file hoặc resource
"{\n" +
" \"resourceType\": \"StructureDefinition\",\n" +
" \"id\": \"patient-vn\",\n" +
" \"url\": \"http://example.org/fhir/StructureDefinition/patient-vn\",\n" +
" \"name\": \"PatientVN\",\n" +
" \"status\": \"active\",\n" +
" \"fhirVersion\": \"5.0.0\",\n" +
" \"kind\": \"resource\",\n" +
" \"abstract\": false,\n" +
" \"type\": \"Patient\",\n" +
" \"baseDefinition\": \"http://hl7.org/fhir/StructureDefinition/Patient\",\n" +
" \"derivation\": \"constraint\",\n" +
" \"differential\": {\n" +
" \"element\": [\n" +
" {\n" +
" \"id\": \"Patient\",\n" +
" \"path\": \"Patient\"\n" +
" },\n" +
" {\n" +
" \"id\": \"Patient.identifier\",\n" +
" \"path\": \"Patient.identifier\",\n" +
" \"min\": 1\n" +
" },\n" +
" {\n" +
" \"id\": \"Patient.name\",\n" +
" \"path\": \"Patient.name\",\n" +
" \"min\": 1\n" +
" },\n" +
" {\n" +
" \"id\": \"Patient.name.family\",\n" +
" \"path\": \"Patient.name.family\",\n" +
" \"min\": 1\n" +
" },\n" +
" {\n" +
" \"id\": \"Patient.name.given\",\n" +
" \"path\": \"Patient.name.given\",\n" +
" \"min\": 1\n" +
" },\n" +
" {\n" +
" \"id\": \"Patient.gender\",\n" +
" \"path\": \"Patient.gender\",\n" +
" \"min\": 1\n" +
" }\n" +
" ]\n" +
" }\n" +
"}";
// Parse profile và thêm vào PrePopulatedValidationSupport
StructureDefinition profile = (StructureDefinition) ctx.newJsonParser().parseResource(profileJson);
prePopulatedSupport.addStructureDefinition(profile);
// Tạo ValidationSupportChain
ValidationSupportChain validationSupportChain = new ValidationSupportChain(
defaultSupport,
prePopulatedSupport,
new InMemoryTerminologyServerValidationSupport(ctx),
new SnapshotGeneratingValidationSupport(ctx)
);
// Tạo instance validator
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ctx);
instanceValidator.setValidationSupport(validationSupportChain);
// Tạo validator và đăng ký module
FhirValidator validator = ctx.newValidator();
validator.registerValidatorModule(instanceValidator);
// Tạo Patient hợp lệ theo profile
Patient validPatient = new Patient();
validPatient.getMeta().addProfile("http://example.org/fhir/StructureDefinition/patient-vn");
validPatient.addIdentifier()
.setSystem("http://hospital.example.org/patients")
.setValue("12345");
validPatient.addName()
.setFamily("Nguyễn")
.addGiven("Văn A");
validPatient.setGender(Enumerations.AdministrativeGender.MALE);
// Validate Patient hợp lệ
ValidationResult validResult = validator.validateWithResult(validPatient);
System.out.println("Valid Patient Validation:");
System.out.println("Is successful: " + validResult.isSuccessful());
validResult.getMessages().forEach(message ->
System.out.println(" - " + message.getSeverity() + ": " + message.getMessage()));
// Tạo Patient không hợp lệ theo profile (thiếu identifier bắt buộc)
Patient invalidPatient = new Patient();
invalidPatient.getMeta().addProfile("http://example.org/fhir/StructureDefinition/patient-vn");
invalidPatient.addName()
.setFamily("Nguyễn")
.addGiven("Văn A");
invalidPatient.setGender(Enumerations.AdministrativeGender.MALE);
// Không thêm identifier mặc dù bắt buộc trong profile
// Validate Patient không hợp lệ
ValidationResult invalidResult = validator.validateWithResult(invalidPatient);
System.out.println("\nInvalid Patient Validation:");
System.out.println("Is successful: " + invalidResult.isSuccessful());
invalidResult.getMessages().forEach(message ->
System.out.println(" - " + message.getSeverity() + ": " + message.getMessage()));
}
}
Kiến trúc của thư viện hapi-fhir-validation
Thư viện hapi-fhir-validation được tổ chức theo kiến trúc module, với các thành phần chính sau:
1. FhirValidator
Interface chính cung cấp các phương thức để thực hiện validation:
Tái sử dụng FhirContext và Validator: Các đối tượng này tốn nhiều tài nguyên để khởi tạo, nên được tạo một lần và tái sử dụng.
Sử dụng caching: Tích hợp CachingValidationSupport để cải thiện hiệu suất.
Validate theo cấp độ: Phân chia validation thành nhiều cấp độ để dễ quản lý:
Basic validation (cấu trúc)
Profile validation (tuân thủ profile)
Terminology validation (mã)
Business rules validation (quy tắc nghiệp vụ)
Tùy chỉnh severity: Điều chỉnh mức độ nghiêm trọng của các lỗi validation.
Tạo custom validation: Triển khai các IValidatorModule tùy chỉnh cho các quy tắc nghiệp vụ đặc thù của ứng dụng.
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.Patient;
import java.util.ArrayList;
import java.util.List;
public class CustomPatientValidator implements IValidatorModule {
@Override
public ValidationResult validateResource(IBaseResource resource) {
List<SingleValidationMessage> messages = new ArrayList<>();
// Kiểm tra nếu resource là Patient
if (resource instanceof Patient) {
Patient patient = (Patient) resource;
// Kiểm tra quy tắc tùy chỉnh: tên phải có ít nhất 2 từ
if (patient.hasName() && patient.getNameFirstRep().hasGiven()) {
String givenName = patient.getNameFirstRep().getGivenAsSingleString();
if (givenName.split(" ").length < 2) {
SingleValidationMessage message = new SingleValidationMessage();
message.setLocationString("Patient.name.given");
message.setSeverity(ResultSeverityEnum.WARNING);
message.setMessage("Given name should have at least 2 words for Vietnamese patients");
messages.add(message);
}
}
// Kiểm tra quy tắc tùy chỉnh: địa chỉ phải có thành phố
if (patient.hasAddress() && !patient.getAddressFirstRep().hasCity()) {
SingleValidationMessage message = new SingleValidationMessage();
message.setLocationString("Patient.address");
message.setSeverity(ResultSeverityEnum.ERROR);
message.setMessage("Address must include city");
messages.add(message);
}
}
return new ValidationResult(messages);
}
}
Xử lý kết quả validation: Phát triển chiến lược xử lý phù hợp cho các vấn đề validation:
Lọc và nhóm các lỗi theo severity
Cung cấp phản hồi rõ ràng cho người dùng
Cho phép ghi đè một số lỗi validation khi cần thiết
Kết hợp với FhirPath: Sử dụng FhirPath để kiểm tra các ràng buộc phức tạp.
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.fhirpath.IFhirPath;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.Patient;
import java.util.List;
public class FhirPathValidationExample {
public static void main(String[] args) {
FhirContext ctx = FhirContext.forR5();
IFhirPath fhirPath = ctx.newFhirPath();
Patient patient = new Patient();
patient.addIdentifier()
.setSystem("http://hospital.example.org/patients")
.setValue("12345");
// Kiểm tra bằng FHIRPath
String expression = "identifier.where(system = 'http://hospital.example.org/patients').exists()";
List<BooleanType> result = fhirPath.evaluate(patient, expression, BooleanType.class);
if (!result.isEmpty() && result.get(0).booleanValue()) {
System.out.println("Patient has valid hospital identifier");
} else {
System.out.println("Patient is missing required hospital identifier");
}
}
}
Tính năng nâng cao của hapi-fhir-validation
1. Validation bất đồng bộ
Đối với các ứng dụng xử lý lượng lớn dữ liệu, validation bất đồng bộ có thể cải thiện hiệu suất:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.Patient;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncValidationExample {
public static void main(String[] args) throws Exception {
FhirContext ctx = FhirContext.forR5();
FhirValidator validator = ctx.newValidator();
// Tạo danh sách patients cần validation
List<Patient> patients = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Patient patient = new Patient();
patient.addName().setFamily("Patient" + i);
patients.add(patient);
}
// Tạo thread pool
ExecutorService executor = Executors.newFixedThreadPool(10);
// Thực hiện validation bất đồng bộ
List<CompletableFuture<ValidationResult>> futures = new ArrayList<>();
for (Patient patient : patients) {
CompletableFuture<ValidationResult> future = CompletableFuture.supplyAsync(
() -> validator.validateWithResult(patient),
executor
);
futures.add(future);
}
// Đợi tất cả validation hoàn thành
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);
// Đợi hoàn thành
allFutures.get();
// Đếm số lượng resources hợp lệ
long validCount = futures.stream()
.map(CompletableFuture::join)
.filter(ValidationResult::isSuccessful)
.count();
System.out.println("Total patients: " + patients.size());
System.out.println("Valid patients: " + validCount);
// Đóng executor
executor.shutdown();
}
}
2. Chỉnh sửa resource để khắc phục lỗi validation
Đôi khi bạn cần tự động khắc phục các lỗi validation:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
public class AutoCorrectValidationExample {
public static void main(String[] args) {
FhirContext ctx = FhirContext.forR5();
// Tạo validator
FhirValidator validator = ctx.newValidator();
validator.registerValidatorModule(new FhirInstanceValidator(ctx));
// Tạo patient có lỗi
Patient patient = new Patient();
// Không thêm tên
// Không thêm identifier
// Validate patient
ValidationResult result = validator.validateWithResult(patient);
// In kết quả validation
System.out.println("Initial validation:");
System.out.println("Successful: " + result.isSuccessful());
System.out.println("Issues found: " + result.getMessages().size());
// Tự động khắc phục lỗi
for (SingleValidationMessage message : result.getMessages()) {
if (message.getSeverity() == ResultSeverityEnum.ERROR) {
// Xử lý các lỗi cụ thể
if (message.getMessage().contains("Patient.name")) {
// Thêm tên
patient.addName().setFamily("Nguyễn").addGiven("Văn A");
System.out.println("Fixed: Added name to patient");
}
if (message.getMessage().contains("Patient.identifier")) {
// Thêm identifier
patient.addIdentifier()
.setSystem("http://hospital.example.org/patients")
.setValue("AUTO-" + System.currentTimeMillis());
System.out.println("Fixed: Added identifier to patient");
}
}
}
// Validate lại sau khi sửa
ValidationResult fixedResult = validator.validateWithResult(patient);
// In kết quả validation sau khi sửa
System.out.println("\nValidation after auto-correction:");
System.out.println("Successful: " + fixedResult.isSuccessful());
System.out.println("Issues found: " + fixedResult.getMessages().size());
// In chi tiết lỗi còn lại
if (!fixedResult.isSuccessful()) {
System.out.println("\nRemaining issues:");
fixedResult.getMessages().forEach(message ->
System.out.println(" - " + message.getSeverity() + ": " + message.getMessage()));
}
}
}
3. Validation từ xa (Remote Validation)
Bạn có thể tích hợp với FHIR server từ xa để thực hiện validation:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.StringType;
public class RemoteValidationExample {
public static void main(String[] args) {
// Khởi tạo FhirContext
FhirContext ctx = FhirContext.forR5();
// Tạo client kết nối đến FHIR server
IGenericClient client = ctx.newRestfulGenericClient("http://hapi.fhir.org/baseR5");
// Tạo patient cần validation
Patient patient = new Patient();
patient.addName().setFamily("Nguyễn").addGiven("Văn A");
// Tạo Parameters cho $validate operation
Parameters inParams = new Parameters();
inParams.addParameter().setName("resource").setResource(patient);
inParams.addParameter().setName("profile").setValue(new StringType("http://hl7.org/fhir/StructureDefinition/Patient"));
// Gọi $validate operation
Parameters outParams = client.operation()
.onType(Patient.class)
.named("$validate")
.withParameters(inParams)
.execute();
// Lấy OperationOutcome từ kết quả
OperationOutcome outcome = (OperationOutcome) outParams.getParameter().get(0).getResource();
// In kết quả validation
System.out.println("Remote validation result:");
if (outcome.getIssue().isEmpty()) {
System.out.println("No issues found - validation successful");
} else {
System.out.println("Issues found:");
outcome.getIssue().forEach(issue ->
System.out.println(" - " + issue.getSeverity() + ": " + issue.getDiagnostics()));
}
}
}
4. Tinh chỉnh các thông báo validation
Bạn có thể tùy chỉnh cách hiển thị các thông báo validation:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
import java.util.HashMap;
import java.util.Map;
public class CustomValidationMessageExample {
public static void main(String[] args) {
FhirContext ctx = FhirContext.forR5();
// Tạo validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ctx);
validator.registerValidatorModule(instanceValidator);
// Tạo patient có lỗi
Patient patient = new Patient();
// Không thêm name
// Thực hiện validation
ValidationResult result = validator.validateWithResult(patient);
// Map để chuyển đổi thông báo lỗi
Map<String, String> errorTranslations = new HashMap<>();
errorTranslations.put("Element 'Patient.name': minimum required = 1",
"Bệnh nhân phải có ít nhất một tên");
errorTranslations.put("Element 'Patient.identifier': minimum required = 1",
"Bệnh nhân phải có ít nhất một mã định danh");
// Hiển thị thông báo tùy chỉnh
System.out.println("Validation issues (user-friendly):");
for (SingleValidationMessage message : result.getMessages()) {
// Lấy thông báo gốc
String originalMessage = message.getMessage();
// Tìm thông báo tùy chỉnh
String customMessage = errorTranslations.getOrDefault(originalMessage, originalMessage);
// Hiển thị mức độ nghiêm trọng và thông báo
String severity = message.getSeverity().name();
String friendlySeverity = "THÔNG TIN";
if (severity.equals("ERROR")) friendlySeverity = "LỖI";
if (severity.equals("WARNING")) friendlySeverity = "CẢNH BÁO";
System.out.println(friendlySeverity + ": " + customMessage);
}
}
}
5. Validation dựa trên context
Đôi khi quy tắc validation phụ thuộc vào context sử dụng:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.Patient;
import java.util.ArrayList;
import java.util.List;
public class ContextAwareValidationExample {
public enum ValidationContext {
REGISTRATION, // Đăng ký ban đầu
EMERGENCY, // Cấp cứu
FOLLOW_UP // Tái khám
}
public static class ContextAwareValidator implements IValidatorModule {
private final ValidationContext context;
public ContextAwareValidator(ValidationContext context) {
this.context = context;
}
@Override
public ValidationResult validateResource(IBaseResource resource) {
List<SingleValidationMessage> messages = new ArrayList<>();
if (resource instanceof Patient) {
Patient patient = (Patient) resource;
switch (context) {
case REGISTRATION:
// Đăng ký cần nhiều thông tin
validateForRegistration(patient, messages);
break;
case EMERGENCY:
// Cấp cứu chỉ cần thông tin cơ bản
validateForEmergency(patient, messages);
break;
case FOLLOW_UP:
// Tái khám cần kiểm tra identifier
validateForFollowUp(patient, messages);
break;
}
}
return new ValidationResult(messages);
}
private void validateForRegistration(Patient patient, List<SingleValidationMessage> messages) {
// Kiểm tra tên đầy đủ
if (!patient.hasName() || !patient.getNameFirstRep().hasFamily()) {
addError(messages, "Patient.name", "Đăng ký cần tên đầy đủ của bệnh nhân");
}
// Kiểm tra địa chỉ
if (!patient.hasAddress()) {
addError(messages, "Patient.address", "Đăng ký cần địa chỉ của bệnh nhân");
}
// Kiểm tra ngày sinh
if (!patient.hasBirthDate()) {
addError(messages, "Patient.birthDate", "Đăng ký cần ngày sinh của bệnh nhân");
}
// Kiểm tra giới tính
if (!patient.hasGender()) {
addError(messages, "Patient.gender", "Đăng ký cần giới tính của bệnh nhân");
}
}
private void validateForEmergency(Patient patient, List<SingleValidationMessage> messages) {
// Trong trường hợp cấp cứu, chỉ cần tên
if (!patient.hasName()) {
addError(messages, "Patient.name", "Cần ít nhất một tên để nhận dạng bệnh nhân");
}
}
private void validateForFollowUp(Patient patient, List<SingleValidationMessage> messages) {
// Tái khám cần identifier
if (!patient.hasIdentifier()) {
addError(messages, "Patient.identifier", "Tái khám cần mã định danh của bệnh nhân");
}
}
private void addError(List<SingleValidationMessage> messages, String path, String message) {
SingleValidationMessage validationMessage = new SingleValidationMessage();
validationMessage.setLocationString(path);
validationMessage.setSeverity(ResultSeverityEnum.ERROR);
validationMessage.setMessage(message);
messages.add(validationMessage);
}
}
public static void main(String[] args) {
FhirContext ctx = FhirContext.forR5();
// Tạo patient cho test
Patient patient = new Patient();
patient.addName().addGiven("A"); // Chỉ có tên, không có họ
// Tạo các validator cho từng context
ContextAwareValidator registrationValidator = new ContextAwareValidator(ValidationContext.REGISTRATION);
ContextAwareValidator emergencyValidator = new ContextAwareValidator(ValidationContext.EMERGENCY);
ContextAwareValidator followUpValidator = new ContextAwareValidator(ValidationContext.FOLLOW_UP);
// Validate trong context đăng ký
ValidationResult registrationResult = registrationValidator.validateResource(patient);
System.out.println("Registration Context Validation:");
registrationResult.getMessages().forEach(message ->
System.out.println(" - " + message.getMessage()));
// Validate trong context cấp cứu
ValidationResult emergencyResult = emergencyValidator.validateResource(patient);
System.out.println("\nEmergency Context Validation:");
if (emergencyResult.getMessages().isEmpty()) {
System.out.println(" - No issues found");
} else {
emergencyResult.getMessages().forEach(message ->
System.out.println(" - " + message.getMessage()));
}
// Validate trong context tái khám
ValidationResult followUpResult = followUpValidator.validateResource(patient);
System.out.println("\nFollow-up Context Validation:");
followUpResult.getMessages().forEach(message ->
System.out.println(" - " + message.getMessage()));
}
}
Tích hợp với các công nghệ khác
Tích hợp với RESTful API
Để validation các resource gửi đến thông qua RESTful API:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.Patient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/patients")
public class PatientController {
private final FhirValidator validator;
private final FhirContext fhirContext;
@Autowired
public PatientController(FhirValidator validator, FhirContext fhirContext) {
this.validator = validator;
this.fhirContext = fhirContext;
}
@PostMapping
public ResponseEntity<?> createPatient(@RequestBody String patientJson) {
try {
// Parse resource từ JSON
Patient patient = fhirContext.newJsonParser().parseResource(Patient.class, patientJson);
// Validate resource
ValidationResult result = validator.validateWithResult(patient);
if (result.isSuccessful()) {
// Xử lý khi resource hợp lệ
// ...
return new ResponseEntity<>("Patient created successfully", HttpStatus.CREATED);
} else {
// Tạo OperationOutcome chứa các lỗi validation
OperationOutcome outcome = new OperationOutcome();
result.getMessages().forEach(message -> {
OperationOutcome.OperationOutcomeIssueComponent issue = outcome.addIssue();
// Map severity
switch (message.getSeverity()) {
case ERROR:
issue.setSeverity(OperationOutcome.IssueSeverity.ERROR);
break;
case WARNING:
issue.setSeverity(OperationOutcome.IssueSeverity.WARNING);
break;
case INFORMATION:
issue.setSeverity(OperationOutcome.IssueSeverity.INFORMATION);
break;
default:
issue.setSeverity(OperationOutcome.IssueSeverity.INFORMATION);
}
issue.setCode(OperationOutcome.IssueType.INVARIANT);
issue.setDiagnostics(message.getMessage());
issue.setLocation(List.of(message.getLocationString()));
});
// Trả về lỗi validation dưới dạng OperationOutcome
String outcomeJson = fhirContext.newJsonParser().setPrettyPrint(true)
.encodeResourceToString(outcome);
return new ResponseEntity<>(outcomeJson, HttpStatus.UNPROCESSABLE_ENTITY);
}
} catch (Exception e) {
return new ResponseEntity<>("Error parsing resource: " + e.getMessage(),
HttpStatus.BAD_REQUEST);
}
}
}
Tích hợp với Message Queue
Validation trong môi trường xử lý message:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.Patient;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
@Component
public class FhirMessageConsumer {
private final FhirContext fhirContext;
private final FhirValidator validator;
@Autowired
public FhirMessageConsumer(FhirContext fhirContext, FhirValidator validator) {
this.fhirContext = fhirContext;
this.validator = validator;
}
@RabbitListener(queues = "fhir.patient.queue")
public void processPatientMessage(@Payload String patientJson) {
try {
// Parse resource từ JSON
Patient patient = fhirContext.newJsonParser().parseResource(Patient.class, patientJson);
// Validate resource
ValidationResult result = validator.validateWithResult(patient);
if (result.isSuccessful()) {
// Xử lý patient hợp lệ
System.out.println("Received valid patient: " + patient.getNameFirstRep().getNameAsSingleString());
// Tiếp tục xử lý...
} else {
// Xử lý patient không hợp lệ
System.out.println("Received invalid patient with " + result.getMessages().size() + " issues");
// Có thể gửi đến dead-letter queue hoặc lưu lỗi
result.getMessages().forEach(message ->
System.out.println(" - " + message.getSeverity() + ": " + message.getMessage()));
}
} catch (Exception e) {
System.err.println("Error processing message: " + e.getMessage());
// Xử lý lỗi
}
}
}
Kết luận
Thư viện hapi-fhir-validation là một công cụ mạnh mẽ và toàn diện để đảm bảo tính hợp lệ của dữ liệu FHIR trong các ứng dụng y tế. Với khả năng validation đa cấp độ từ cấu trúc đến nội dung, terminologies và profiles, thư viện này giúp đảm bảo chất lượng dữ liệu và tính tương tác giữa các hệ thống y tế.
Các lợi ích chính của thư viện bao gồm:
Đa dạng loại validation: Hỗ trợ nhiều cấp độ validation từ cấu trúc đến nội dung và terminologies
Tích hợp với profiles: Validation dựa trên StructureDefinition profiles cho phép tùy chỉnh các ràng buộc
Kiến trúc module: Thiết kế module hóa cho phép mở rộng và tùy chỉnh
API trực quan: API dễ sử dụng với FhirValidator và ValidationResult
Hiệu suất tốt: Cơ chế caching và hỗ trợ xử lý bất đồng bộ
Khi xây dựng các ứng dụng y tế với FHIR, việc tích hợp hapi-fhir-validation giúp đảm bảo dữ liệu tuân thủ các tiêu chuẩn và quy định, đồng thời cải thiện khả năng tương tác giữa các hệ thống khác nhau trong hệ sinh thái chăm sóc sức khỏe.