hapi-fhir-validation
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)
Hỗ trợ multiple profiles cho một resource
2. Validation terminologies (ValueSet, CodeSystem)
Một phần quan trọng của FHIR là sử dụng các bộ mã chuẩn (terminologies) như LOINC, SNOMED CT, ICD-10:
Kiểm tra tính hợp lệ của mã trong CodeSystem
Kiểm tra mã có thuộc ValueSet được chỉ định không
Kiểm tra binding strength (required, extensible, preferred, example)
Hỗ trợ kiểm tra hierarchical code systems (như SNOMED CT)
3. FhirValidator và ValidationResult APIs
Thư viện cung cấp API trực quan và dễ sử dụng:
FhirValidator
: Interface chính để thực hiện validationValidationResult
: Đối tượng chứa kết quả validation chi tiếtSingleValidationMessage
: Thông tin về từng lỗi/cảnh báoHỗ trợ nhiều cấp độ severity (INFORMATION, WARNING, ERROR, FATAL)
4. Modules validation mở rộng
Thư viện được thiết kế theo kiến trúc module, cho phép sử dụng nhiều validator khác nhau:
SchemaBaseValidator
: Kiểm tra tuân thủ XML SchemaSchematronBaseValidator
: Kiểm tra tuân thủ Schematron rulesFhirInstanceValidator
: Module validation chính, dựa trên StructureDefinition
5. Tích hợp với FHIR profiles validation
Hỗ trợ đầy đủ các tính năng của FHIR profiles:
Validation dựa trên snapshot và differential views
Hỗ trợ slicing (phân chia phần tử theo quy tắc)
Hỗ trợ constraints kế thừa
Validation extensions
Cài đặt và sử dụng
Thêm vào Maven Project
<!-- Thư viện cơ sở - luôn cần thiết -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>6.4.0</version>
</dependency>
<!-- Thư viện validation -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation</artifactId>
<version>6.4.0</version>
</dependency>
<!-- Structures cho phiên bản FHIR bạn đang sử dụng (ví dụ R5) -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r5</artifactId>
<version>6.4.0</version>
</dependency>
<!-- Tài nguyên validation -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-r5</artifactId>
<version>6.4.0</version>
</dependency>
Thêm vào Gradle Project
implementation 'ca.uhn.hapi.fhir:hapi-fhir-base:6.4.0'
implementation 'ca.uhn.hapi.fhir:hapi-fhir-validation:6.4.0'
implementation 'ca.uhn.hapi.fhir:hapi-fhir-structures-r5:6.4.0'
implementation 'ca.uhn.hapi.fhir:hapi-fhir-validation-resources-r5:6.4.0'
Ví dụ sử dụng hapi-fhir-validation
Ví dụ 1: Validation cơ bản
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:
ValidationResult validateWithResult(IBaseResource resource);
ValidationResult validateWithResult(String input, FhirFormat format);
2. IValidatorModule
Interface cho các module validator. HAPI FHIR cung cấp các triển khai:
SchemaBaseValidator: Kiểm tra tuân thủ XML Schema
SchematronBaseValidator: Kiểm tra tuân thủ Schematron rules
FhirInstanceValidator: Module validator chính, dựa trên StructureDefinition và terminologies
3. ValidationSupportChain
Cung cấp khả năng kết hợp nhiều IValidationSupport, với các triển khai phổ biến:
DefaultProfileValidationSupport: Hỗ trợ validation với profiles mặc định của FHIR
PrePopulatedValidationSupport: Lưu trữ và cung cấp các tài nguyên validation
InMemoryTerminologyServerValidationSupport: Cung cấp terminology server trong bộ nhớ
CachingValidationSupport: Caching để cải thiện hiệu suất
SnapshotGeneratingValidationSupport: Tạo snapshot từ differential definitions
4. ValidationResult và SingleValidationMessage
Cấu trúc để lưu trữ kết quả validation:
ValidationResult: Chứa danh sách các messages và phương thức kiểm tra kết quả
SingleValidationMessage: Thông điệp validation cụ thể (location, severity, message)
Tích hợp với Spring Boot
Để tích hợp hapi-fhir-validation
với Spring Boot, bạn có thể tạo một Bean configuration như sau:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.validation.FhirValidator;
import org.hl7.fhir.common.hapi.validation.support.*;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FhirValidationConfig {
@Bean
public FhirContext fhirContext() {
return FhirContext.forR5();
}
@Bean
public DefaultProfileValidationSupport defaultProfileValidationSupport(FhirContext fhirContext) {
return new DefaultProfileValidationSupport(fhirContext);
}
@Bean
public InMemoryTerminologyServerValidationSupport terminologyServerValidationSupport(FhirContext fhirContext) {
return new InMemoryTerminologyServerValidationSupport(fhirContext);
}
@Bean
public PrePopulatedValidationSupport prePopulatedValidationSupport(FhirContext fhirContext) {
return new PrePopulatedValidationSupport(fhirContext);
}
@Bean
public SnapshotGeneratingValidationSupport snapshotGeneratingValidationSupport(FhirContext fhirContext) {
return new SnapshotGeneratingValidationSupport(fhirContext);
}
@Bean
public ValidationSupportChain validationSupportChain(
DefaultProfileValidationSupport defaultSupport,
InMemoryTerminologyServerValidationSupport terminologySupport,
PrePopulatedValidationSupport prePopulatedSupport,
SnapshotGeneratingValidationSupport snapshotSupport) {
return new ValidationSupportChain(
defaultSupport,
terminologySupport,
prePopulatedSupport,
snapshotSupport
);
}
@Bean
public FhirInstanceValidator fhirInstanceValidator(FhirContext fhirContext, ValidationSupportChain validationSupportChain) {
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(fhirContext);
instanceValidator.setValidationSupport(validationSupportChain);
return instanceValidator;
}
@Bean
public FhirValidator fhirValidator(FhirContext fhirContext, FhirInstanceValidator instanceValidator) {
FhirValidator validator = fhirContext.newValidator();
validator.registerValidatorModule(instanceValidator);
return validator;
}
}
Sau đó bạn có thể inject FhirValidator vào các service:
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.Patient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PatientValidationService {
private final FhirValidator validator;
@Autowired
public PatientValidationService(FhirValidator validator) {
this.validator = validator;
}
public ValidationResult validatePatient(Patient patient) {
return validator.validateWithResult(patient);
}
public boolean isPatientValid(Patient patient) {
ValidationResult result = validator.validateWithResult(patient);
return result.isSuccessful();
}
}
Các loại validation trong FHIR
Thư viện hapi-fhir-validation
hỗ trợ nhiều loại validation khác nhau:
1. Structural Validation
Kiểm tra cấu trúc của resource theo đặc tả FHIR:
Các thành phần bắt buộc
Kiểu dữ liệu đúng
Cardinality hợp lệ
2. Content Validation
Kiểm tra nội dung của resource:
References hợp lệ
Ràng buộc chéo giữa các trường
FHIRPath invariants
3. Business Rules Validation
Kiểm tra theo các quy tắc nghiệp vụ:
Ràng buộc theo profiles tùy chỉnh
Invariants phức tạp
4. Terminology Validation
Kiểm tra mã và terminologies:
Mã có tồn tại trong CodeSystem
Mã thuộc ValueSet được chỉ định
Binding strength (required, extensible, preferred, example)
Best Practices khi sử dụng hapi-fhir-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.
Last updated