Using Enums to Make Flat File Parsing in Java More Maintainable
Delimited flat-file parsing often leads to brittle index-based code. In this post, I show how enums make field positions easier to read and maintain.
Direct indexing #
A pattern I see frequently in code is direct indexing, as shown below. The reason is that the developer has chosen brevity over semantic clarity. It is fast to write, it’s short, and it may even make sense at the time of writing. However, it suffers from a number of issues as well, primarily the issue of magic numbers. The numbers used in the indexing say absolutely nothing about what you are fetching and make the code harder to read and understand, which in turn leads to a bigger cognitive load (more to keep track of).
Another big issue is that this type of pattern tends to be repeated throughout the code base, making code changes brittle and more problematic due to breaking the DRY (don’t repeat yourself) principles.
String[] split = delimitedData.split("\\|");
DbData existingData = dbHandler.getExistingData(
split[2],
split[7],
split[8],
split[9]
);
Direct indexing is compact, but brittle and hard to scan.
The next step to create a more maintainable code base, is usually to name the indexes using local variables to make the intent clearer.
Local variables #
Extracting split indices into local variables, like the example below, prioritizes semantic clarity over brevity, making intent immediately clearer, effectively eliminating magic numbers, but there are still issues using this sort of pattern. For single use, this works well, but if this pattern is re-used elsewhere in the code base, you will still have scattered field mappings, once again breaking the principles of DRY.
String[] split = delimitedData.split("\\|");
String id = split[2];
String date = split[7];
String time = split[8];
String reason = split[9];
DbData existingData = dbHandler.getExistingData(
id,
date,
time,
reason
);
Local variables improve readability at the call site, but field mappings are still scattered across the code base.
Enum mapping #
The most resilient pattern for mapping indices I’ve seen is using an enum to create the field mappings (see example below). This is a pattern I have myself used many times, and as long as the mapping is simple, meaning no data validation or parsing is required, it is an effective strategy that clarifies intent, centralizes index mappings and conforms well to the principles of DRY. There are drawbacks to this pattern as well. The most significant is a small performance hit due to the use of an enum.
public enum FlatFileField {
ID(2),
DATE(7),
TIME(8),
REASON(9);
private final int index;
FlatFileField(int index) {
this.index = index;
}
public int index() {
return this.index;
}
public static String getFieldValue(String[] split, FlatFileField field) {
return split[field.index()];
}
}
String[] split = delimitedData.split("\\|");
DbData existingData = dbHandler.getExistingData(
FlatFileField.getFieldValue(split, FlatFileField.ID),
FlatFileField.getFieldValue(split, FlatFileField.DATE),
FlatFileField.getFieldValue(split, FlatFileField.TIME),
FlatFileField.getFieldValue(split, FlatFileField.REASON)
);
Comparison #
| Approach | Pros | Cons |
|---|---|---|
| Direct indexing | Concise | Uses magic numbers, hard to maintain, higher cognitive load |
| Local variables | Readable at call site | Field mapping still scattered |
| Enum mapping | Centralized field positions, clearer intent | Requires an additional enum |
Takeaway #
Enums are a simple way to replace magic numbers with meaningful names when working with delimited data. They improve readability and centralize field positions. When parsing logic grows beyond simple positional access, a dedicated parser or DTO is usually a better choice.