Java EDI Parser User Guide
Overview
The EDI Parser for Java reads X12 EDI and maps it to regular Java business objects. Instead of navigating loops, segments, elements, and qualifiers directly, your application can work with objects such as Claim, Payment, MemberCoverage, ClaimStatus, ServiceLine, and OrgOrPerson.
The general parsing pattern is the same for every supported transaction:
- Create an
EdiParserfor aFileorReader. - Optionally enable validation.
- Parse the input in chunks.
- Iterate over the root objects returned for each chunk.
- Process each root object according to its Java type.
- Continue until
EdiParsingResults.isDone()returnstrue. - Close the parser, preferably with try-with-resources.
Before using the examples, add the parser library to your project and configure a license key.
Parsing Flow
Use parse(int chunkSize) for files that may contain many transactions or business objects. Chunked parsing limits the amount of parsed data held in memory at one time and lets your application process each batch before reading more input.
try (var parser = new EdiParser(ediFile)
.isValidationMode(true)) {
EdiParsingResults results;
do {
results = parser.parse(20);
for (var rootObj : results.rootObjs()) {
if (rootObj instanceof Claim claim) {
processClaim(claim);
}
else if (rootObj instanceof EdiTransaction transaction) {
processTransaction(transaction);
}
else if (rootObj instanceof InterchangeControl interchange) {
processInterchange(interchange);
}
else if (rootObj instanceof FunctionalGroup group) {
processFunctionalGroup(group);
}
else if (rootObj instanceof ValidationIssue issue) {
processValidationIssue(issue);
}
else {
throw new IllegalStateException(
"Unexpected root object: " + rootObj.getClass().getName());
}
}
} while (!results.isDone());
}
rootObjs() preserves the order in which root objects appear in the EDI file. This is useful when a file contains multiple interchanges, functional groups, transactions, or transaction-specific object types.
For a small file, parse() reads all available input in one call. You can then use rootObjs() or a typed collection such as claims():
try (var parser = new EdiParser(ediFile)) {
EdiParsingResults results = parser.parse();
for (Claim claim : results.claims()) {
processClaim(claim);
}
}
Prefer parse() and parse(int) for new code. The older transaction-specific methods parse837(int) and parse835(int) are deprecated.
Root Objects
Every chunk can contain shared envelope and transaction objects:
InterchangeControlrepresents theISA/IEAinterchange envelope.FunctionalGrouprepresents theGS/GEfunctional group.EdiTransactioncontains transaction-level data from theST/SEtransaction.ValidationIssuerepresents an issue associated with the transaction or EDI structure.
The transaction-specific business root objects depend on the EDI transaction:
| EDI transaction | Business root objects |
|---|---|
837P, 837I, and 837D |
Claim |
835 |
Payment and ProviderAdjustment |
834 |
MemberCoverage |
277CA |
ClaimStatus, ProviderStatus, and ReceiverStatus |
EdiParsingResults also provides typed collections when file order is not important:
claims()payments()providerAdjustments()memberCoverages()claimStatuses()ediTransactions()
Use rootObjs() when you need all object types in source order. Use the typed collections when you only need one kind of business object from the current chunk.
Object Model
The parser returns plain Java objects such as Claim, Payment, ServiceLine, PatientSubscriber, OrgOrPerson, and CodeEntity. These objects abstract X12 loops and segment layouts while retaining the data mapped from the source EDI.
For example, a Claim is the root of the object graph for an 837 claim. It combines data from the CLM loop, including references, dates, providers, subscriber and patient information, diagnoses, and service lines. Each ServiceLine contains line-level fields such as procedure or revenue codes, dates, amounts, quantities, providers, and adjustments.
The same object model is used by the Java EDI generation API, so parsed objects can be modified and written back to EDI.
See the EDI schema documentation for complete field definitions and X12 mappings:
Parse 837 Claims
Claim is the business root object for professional, institutional, and dental claims. The same parsing flow works for each 837 transaction type.
private void processClaim(Claim claim) {
String patientControlNumber = claim.patientControlNumber();
BigDecimal chargeAmount = claim.chargeAmount();
OrgOrPerson billingProvider = claim.billingProvider();
String billingProviderNpi = billingProvider.identifier();
PatientSubscriber subscriber = claim.subscriber();
String subscriberId = subscriber.person().identifier();
String payerId = subscriber.payer().identifier();
for (var diagnosis : claim.diags()) {
String diagnosisCode = diagnosis.code();
}
for (ServiceLine line : claim.lines()) {
String procedureCode = line.procedure() == null
? null
: line.procedure().code();
String revenueCode = line.revenueCode() == null
? null
: line.revenueCode().code();
LocalDate serviceDate = line.serviceDateFrom();
BigDecimal lineChargeAmount = line.chargeAmount();
}
}
Professional lines normally use procedure codes. Institutional lines can use revenue codes, and their procedure code may be absent. Institutional claims can also contain claim-level procedures, occurrence codes, value codes, occurrence spans, and present-on-admission indicators.
The complete example parses both 837P and 837I, processes transaction metadata, providers, subscribers, patients, diagnoses, institutional codes, service lines, and validation issues.
View the complete 837 parsing example in GitHub.
Parse 835 Payments
An 835 can produce two business root types:
Paymentrepresents one adjudicated claim from aCLPloop.ProviderAdjustmentrepresents a provider-levelPLBadjustment that is not associated with one specific claim.
if (rootObj instanceof Payment payment) {
String patientControlNumber = payment.patientControlNumber();
BigDecimal billedAmount = payment.chargeAmount();
BigDecimal paidAmount = payment.paymentAmount();
AdjudicatedClaimStatus status = payment.claimStatus();
for (var adjustment : payment.adjustments()) {
String reasonCode = adjustment.reasonCode();
BigDecimal amount = adjustment.amount();
}
for (ServiceLine line : payment.lines()) {
BigDecimal lineBilledAmount = line.chargeAmount();
BigDecimal linePaidAmount = line.paidAmount();
}
}
else if (rootObj instanceof ProviderAdjustment adjustment) {
String providerId = adjustment.providerIdentifier();
LocalDate fiscalPeriodDate = adjustment.fiscalPeriodDate();
}
Payment transactions also contain payment-level fields such as the payment method, payment date, check or EFT trace number, and total payment amount.
View the complete 835 parsing example in GitHub.
Parse 834 Member Coverage
MemberCoverage represents an INS member detail loop and its related segments. It contains the sponsor, insurer, member, policy or group information, and a list of HealthCoverage objects from the 2300 loops.
if (rootObj instanceof MemberCoverage memberCoverage) {
String sponsorId = memberCoverage.sponsor().identifier();
String insurerId = memberCoverage.insurer().identifier();
String memberId = memberCoverage.identifier();
String groupNumber = memberCoverage.groupOrPolicyNumber();
Member member = memberCoverage.member();
for (HealthCoverage coverage : memberCoverage.healthCoverages()) {
processHealthCoverage(coverage);
}
}
View the complete 834 parsing example in GitHub.
Parse 277CA Claim Acknowledgments
A 277CA can report acknowledgment status at several levels:
ClaimStatuscontains a claim’s patient control number, billing provider, patient, claim status information, and any service-line statuses.ProviderStatusreports status for a provider rather than an individual claim.ReceiverStatusreports a transaction-level rejection when no claim-level statuses are available.
Each status can contain multiple StatusInfo objects, and each StatusInfo can contain multiple category and status code pairs.
if (rootObj instanceof ClaimStatus claimStatus) {
String patientControlNumber = claimStatus.patientControlNumber();
for (StatusInfo status : claimStatus.statusInfos()) {
StatusActionType action = status.actionType();
for (StatusCodeInfo code : status.statusCodeInfos()) {
String categoryCode = code.categoryCode();
String statusCode = code.statusCode();
}
}
for (ServiceLineStatus lineStatus : claimStatus.serviceLineStatuses()) {
String lineControlNumber = lineStatus.controlNumber();
}
}
The complete example covers normal claim acknowledgments, service-line rejections, provider-level statuses, and receiver-level transaction rejections.
View the complete 277CA parsing example in GitHub.
Validation
Enable validation when creating the parser:
try (var parser = new EdiParser(ediFile)
.isValidationMode(true)) {
EdiParsingResults results = parser.parse();
for (ValidationIssue issue : results.allValidationIssues()) {
System.out.println(issue.toFullMessage());
}
}
Validation issues can be associated with different levels of the parsed data:
results.validationIssues()contains transaction-level issues for the current chunk.- Transaction-level
ValidationIssueobjects also appear inrootObjs()in source order. - Business roots such as
Claim,Payment, andMemberCoverageexpose their ownvalidationIssues()collections. results.allValidationIssues()combines transaction-level and object-level validation issues for the current chunk.
The parser logs validation issues automatically. Use these collections when your application needs to store, display, filter, or reject data based on validation results.
Parsing failures and malformed EDI details are also available from results.parsingIssues().
Enums and EDI Qualifiers
The parser translates many EDI qualifiers and codes into Java enums. For example:
- The
DMGgender code is represented by GenderType. - The
NM1entity identifier code is represented by EntityRole. - The
CLM05place-of-service value is represented by PlaceOfServiceType.
Claim claim = results.claims().get(0);
PlaceOfServiceType placeOfService = claim.placeOfServiceType();
PatientSubscriber subscriber = claim.subscriber();
EntityRole role = subscriber.person().entityRole();
EntityType entityType = subscriber.person().entityType();
GenderType gender = subscriber.person().gender();
// Convert an enum back to its EDI qualifier or code.
String ediValue = role.ediValue();
View the complete enum parsing example in GitHub.
Segment-Level Parsing
Use the segment API when you need the raw segment tree, want to find elements by segment and position, or are working with a transaction that does not have a business object model.
The segment example shows how to find segments, navigate child loops, read elements by position or name, format segment trees, and convert segments to Jackson JsonNode objects.
View the complete segment parsing example in GitHub.
Run the Examples
The complete examples are JUnit tests in the hdi.edi.parser package. After configuring your license, clone the repository and run the suite from the java-parser directory:
./gradlew test
The examples read EDI files from the repository’s top-level edi_files directory. The shared paths and chunk size are defined in ParsingExampleHelper.java.
You can verify the installed parser version and license information with VersionAndLicenseInfoExample.java.