Case Study
Legacy Flat-File to JSON Bridge
Modern zero-dependency Java bridge that ingests fixed-width mainframe-style records and serializes nested JSON — records, text blocks, manual byte-range parsing.
- Java
- NIO
- records
- text blocks
- java
- legacy
- mainframe
- json
- enterprise
- zero-dependency
Overview
This project simulates modernizing legacy mainframe data. It ingests an ugly, fixed-width sequential file — a format common in z/OS, banking, and DB2 batch dumps — and parses, sanitizes, and serializes it into structured, nested JSON.
To keep with the theme of Project 1 and Project 2, this is written in modern, zero-dependency Java (utilizing features like records and text blocks). It handles complex parsing challenges natively, including manual byte-range slicing, strict data type conversion, whitespace trimming, and custom JSON serialization.
What it implements
- Fixed-width field slicing — strict character-coordinate parsing for legacy batch layouts
- Implicit decimal conversion — balance stored as cents (
0000452050→$4,520.50) - Code-table mapping —
CHK/SVGtype codes andA/Istatus flags - Malformed line tolerance — skips corrupt rows with terminal warnings
- Native JSON output — no Jackson or Gson; text blocks format each record
Schema layout
The bridge parses fixed positions based on this layout:
| Field | Positions | Length | Notes |
|---|---|---|---|
| ID | 0–9 | 10 | Account identifier |
| Name | 10–29 | 20 | Customer name, padded |
| Balance | 30–39 | 10 | Implicit cents (no decimal point) |
| Type code | 40–42 | 3 | CHK = Checking, SVG = Savings |
| Status | 43 | 1 | A = Active, I = Inactive |
| Last updated | 44–53 | 10 | YYYY-MM-DD |
Each line must be exactly 54 characters.
Project setup
1. Create project files
mkdir legacy-bridge && cd legacy-bridge
touch legacy_accounts.txt LegacyBridge.java
2. Sample fixed-width data
Open legacy_accounts.txt and paste the following. Fixed-width files rely on exact character spacing — columns must line up exactly:
1001429584COLE, DAVID 0000452050CHKA2026-04-12
2008511409SMITH, AMANDA 0012500075SVGA2025-11-30
3004419200RODRIGUEZ, CARLOS 0000000000CHKI2026-01-15
4009725513CHENG, WEI 0000089000SVGA2026-05-01
The code (LegacyBridge.java)
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class LegacyBridge {
// --- Modern Java Data Structures ---
public record Account(
String id,
String fullName,
double balance,
String accountType,
boolean isActive,
String lastUpdated
) {
// Natively formats the record into a clean JSON object string
public String toJsonString() {
return """
{
"id": "%s",
"customerName": "%s",
"balance": %.2f,
"type": "%s",
"active": %b,
"lastUpdated": "%s"
}""".formatted(id, fullName, balance, accountType, isActive, lastUpdated);
}
}
// --- Parsing Core Logic ---
public static Account parseLine(String line) {
// Protect against corrupt or truncated batch lines
if (line == null || line.length() < 54) {
throw new IllegalArgumentException("Invalid layout segment: Line length must be exactly 54 characters.");
}
// Slice fields strictly by character coordinates
String rawId = line.substring(0, 10).trim();
String rawName = line.substring(10, 30).trim();
String rawBalance = line.substring(30, 40).trim();
String rawType = line.substring(40, 43).trim();
String rawStatus = line.substring(43, 44).trim();
String rawDate = line.substring(44, 54).trim();
// Sanitize and transform raw legacy strings to modern data types
double balanceInDollars = Double.parseDouble(rawBalance) / 100.0;
String accountType = switch (rawType) {
case "CHK" -> "Checking";
case "SVG" -> "Savings";
default -> "Unknown (" + rawType + ")";
};
boolean isActive = rawStatus.equalsIgnoreCase("A");
return new Account(rawId, rawName, balanceInDollars, accountType, isActive, rawDate);
}
// --- Orchestration Routine ---
public static void main(String[] args) {
String inputFileName = "legacy_accounts.txt";
Path inputPath = Paths.get(inputFileName);
System.out.println("\033[96m⚙️ Initializing Legacy Flat-File Transformation Bridge...\033[0m");
if (!Files.exists(inputPath)) {
System.err.println("\033[91mError: Target sequential file '" + inputFileName + "' missing.\033[0m");
System.exit(1);
}
try {
// Stream the file lines natively using NIO
List<String> lines = Files.readAllLines(inputPath);
List<Account> parsedAccounts = new ArrayList<>();
for (String line : lines) {
if (line.isBlank()) continue;
try {
parsedAccounts.add(parseLine(line));
} catch (Exception e) {
System.err.println("\033[93m[Skipping Malformed Line] " + e.getMessage() + "\033[0m");
}
}
// Serialize the array collection manually to enforce formatting without third-party tools
String jsonOutput = parsedAccounts.stream()
.map(Account::toJsonString)
.collect(Collectors.joining(",\n", "[\n", "\n]"));
// Write transformed dataset back to disk
Path outputPath = Paths.get("accounts_transformed.json");
Files.writeString(outputPath, jsonOutput);
// Display success telemetry directly to terminal output
System.out.println("\033[92mSuccessfully modernized data structures!\033[0m");
System.out.println("📄 Output generated: " + outputPath.toAbsolutePath());
System.out.println("\n--- Modernized JSON Payload Output Sample ---");
System.out.println(jsonOutput);
} catch (IOException e) {
System.err.println("\033[91mCritical System Failure executing data transformation pipeline:\033[0m " + e.getMessage());
}
}
}
Execution
Single-file source execution — no Maven, Gradle, or dependency tree required:
java LegacyBridge.java
The bridge parses legacy_accounts.txt and writes accounts_transformed.json to the working directory.
Example output shape:
[
{
"id": "1001429584",
"customerName": "COLE, DAVID",
"balance": 4520.50,
"type": "Checking",
"active": true,
"lastUpdated": "2026-04-12"
}
]
Why this shines on a portfolio
- Solves an enterprise problem — proves you can ingest rigid legacy layouts: implicit decimal shifts, alpha-numeric state flags, and padded name fields that underpin older corporate stacks
- Advanced Java structures — text blocks and records over boilerplate POJOs and string concatenation
- Pairs with real modernization work — same parsing discipline as The Technomancer’s Path COBOL training platform, different layer of the pipeline
Related work
- Project 1: Markdown API Router — zero-dependency TypeScript
- Project 2: Log Anomaly CLI — zero-dependency Python + Ollama
- Project 4: Recursive Hierarchy Builder — PostgreSQL recursive CTE to nested JSON
- Project 5: Concurrent Link Sweeper — Go goroutines for network sweeps
- Project 6: Local Context Server — MCP-style Linux tool gateway for agents
- Project 7: Multi-Tenant Migration Engine — 2PC fleet migrations for Gnomad CRM
- Technomancer’s Path — enterprise legacy talent pipeline case study