Skip to the content.

Examples

Each example below is self-contained and demonstrates a specific feature. All examples use the same entry point:

FixedFormatManager manager = new FixedFormatManagerImpl();

Example 1 — Financial record with decimals and signed amounts

This example shows @FixedFormatDecimal for implicit-decimal storage and @FixedFormatNumber for signed values — common requirements in financial and mainframe file formats.

With useDecimalDelimiter = false, the decimal point is not stored in the string; instead the last decimals digits are treated as the fractional part. This saves a character per field compared to storing "123.50".

@Record
public class TransactionRecord {

  private BigDecimal amount;
  private BigDecimal balance;

  // "0001250" → 12.50  (last 2 digits are decimals, no delimiter stored)
  @Field(offset = 1, length = 7, align = Align.RIGHT, paddingChar = '0')
  @FixedFormatDecimal(decimals = 2, useDecimalDelimiter = false)
  public BigDecimal getAmount() { return amount; }
  public void setAmount(BigDecimal amount) { this.amount = amount; }

  // "-000500" → -5.00  (sign prepended, takes up one of the 7 characters)
  @Field(offset = 8, length = 7, align = Align.RIGHT, paddingChar = '0')
  @FixedFormatDecimal(decimals = 2, useDecimalDelimiter = false)
  @FixedFormatNumber(sign = Sign.PREPEND)
  public BigDecimal getBalance() { return balance; }
  public void setBalance(BigDecimal balance) { this.balance = balance; }
}
String line = "0001250-000500";
TransactionRecord record = manager.load(TransactionRecord.class, line);

System.out.println(record.getAmount());   // 12.50
System.out.println(record.getBalance());  // -5.00

record.setAmount(new BigDecimal("99.99"));
System.out.println(manager.export(record));
// "0009999-000500"

Decimal annotation options at a glance:

useDecimalDelimiter String stored Parsed value
false (default) "001250" 12.50
true "012.50" 12.50

Example 2 — Boolean flags with custom true/false values

The default boolean representation is "T" / "F". Use @FixedFormatBoolean to override this — for example to match a legacy format that uses "Y" / "N" or "1" / "0".

@Record
public class StatusRecord {

  private Boolean active;
  private Boolean verified;

  @Field(offset = 1, length = 1)
  @FixedFormatBoolean(trueValue = "Y", falseValue = "N")
  public Boolean getActive() { return active; }
  public void setActive(Boolean active) { this.active = active; }

  @Field(offset = 2, length = 1)
  @FixedFormatBoolean(trueValue = "1", falseValue = "0")
  public Boolean getVerified() { return verified; }
  public void setVerified(Boolean verified) { this.verified = verified; }
}
String line = "Y1";
StatusRecord record = manager.load(StatusRecord.class, line);

System.out.println(record.getActive());    // true
System.out.println(record.getVerified()); // true

record.setActive(false);
System.out.println(manager.export(record));
// "N1"

Example 3 — Right-aligned, zero-padded integers

Fixed-width formats often store numbers right-aligned and zero-padded so that every record has the same layout. The align and paddingChar attributes on @Field control this.

@Record
public class OrderRecord {

  private String productCode;
  private Integer quantity;
  private Integer unitPriceCents;

  // Left-aligned text, padded with spaces (the default)
  @Field(offset = 1, length = 10)
  public String getProductCode() { return productCode; }
  public void setProductCode(String productCode) { this.productCode = productCode; }

  // Right-aligned number, padded with zeros on the left
  @Field(offset = 11, length = 5, align = Align.RIGHT, paddingChar = '0')
  public Integer getQuantity() { return quantity; }
  public void setQuantity(Integer quantity) { this.quantity = quantity; }

  // Right-aligned, zero-padded price in cents
  @Field(offset = 16, length = 8, align = Align.RIGHT, paddingChar = '0')
  public Integer getUnitPriceCents() { return unitPriceCents; }
  public void setUnitPriceCents(Integer unitPriceCents) { this.unitPriceCents = unitPriceCents; }
}
String line = "WIDGET-A  000120000150";
OrderRecord record = manager.load(OrderRecord.class, line);

System.out.println(record.getProductCode());    // "WIDGET-A"
System.out.println(record.getQuantity());       // 12
System.out.println(record.getUnitPriceCents()); // 150

record.setQuantity(5);
System.out.println(manager.export(record));
// "WIDGET-A  000050000150"

Alignment at a glance:

align Padding side Trim side Typical use
Align.LEFT (default) Right Right Text fields
Align.RIGHT Left Left Numeric fields

Example 4 — Processing a file line by line

fixedformat4j maps a single line to a Java object. To process a whole file, loop through the lines yourself and call manager.load(...) for each one.

FixedFormatManager manager = new FixedFormatManagerImpl();
List<EmployeeRecord> employees = new ArrayList<>();

try (BufferedReader reader = new BufferedReader(new FileReader("employees.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        if (!line.isBlank()) {
            employees.add(manager.load(EmployeeRecord.class, line));
        }
    }
}

// Process the loaded records
for (EmployeeRecord emp : employees) {
    System.out.println(emp.getName() + " — ID: " + emp.getEmployeeId());
}

If a single file contains records of more than one type, read a discriminator field first and branch to the appropriate class:

while ((line = reader.readLine()) != null) {
    String recordType = line.substring(0, 1); // type code in column 1
    if ("E".equals(recordType)) {
        employees.add(manager.load(EmployeeRecord.class, line));
    } else if ("M".equals(recordType)) {
        managers.add(manager.load(ManagerRecord.class, line));
    }
}

Example 5 — Custom formatter

When no built-in formatter fits your field type, implement FixedFormatter<T> directly.

This example maps a field to java.time.YearMonth (e.g. "2024-03" for March 2024):

public class YearMonthFormatter extends AbstractFixedFormatter<YearMonth> {

  @Override
  public YearMonth asObject(String value, FormatInstructions instructions) {
    // 'value' arrives already stripped of padding by the base class
    return YearMonth.parse(value, DateTimeFormatter.ofPattern("yyyy-MM"));
  }

  @Override
  public String asString(YearMonth value, FormatInstructions instructions) {
    return value.format(DateTimeFormatter.ofPattern("yyyy-MM"));
  }
}

Wire it in with the formatter attribute on @Field:

@Record
public class ReportRecord {

  private YearMonth reportPeriod;

  @Field(offset = 1, length = 7, formatter = YearMonthFormatter.class)
  public YearMonth getReportPeriod() { return reportPeriod; }
  public void setReportPeriod(YearMonth reportPeriod) { this.reportPeriod = reportPeriod; }
}
String line = "2024-03";
ReportRecord record = manager.load(ReportRecord.class, line);

System.out.println(record.getReportPeriod()); // 2024-03

record.setReportPeriod(YearMonth.of(2025, 1));
System.out.println(manager.export(record));
// "2025-01"

For access to supplementary annotation data inside the formatter (e.g., @FixedFormatPattern), use the FormatInstructions argument:

String pattern = instructions.getFixedFormatPatternData().getPattern();

See the FAQ for more details on the FixedFormatter interface.


Home Quick Start Usage Get It FAQ