{"id":26022969,"url":"https://github.com/agrison/legacy4j","last_synced_at":"2026-04-20T15:05:03.929Z","repository":{"id":145285697,"uuid":"1707891","full_name":"agrison/legacy4j","owner":"agrison","description":"Legacy flat files for Java","archived":false,"fork":false,"pushed_at":"2013-05-23T05:34:50.000Z","size":144,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-06T10:43:04.411Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/agrison.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-05-05T19:00:09.000Z","updated_at":"2016-04-25T12:08:21.000Z","dependencies_parsed_at":"2023-03-22T19:03:14.336Z","dependency_job_id":null,"html_url":"https://github.com/agrison/legacy4j","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/agrison/legacy4j","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrison%2Flegacy4j","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrison%2Flegacy4j/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrison%2Flegacy4j/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrison%2Flegacy4j/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agrison","download_url":"https://codeload.github.com/agrison/legacy4j/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrison%2Flegacy4j/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32052560,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-06T10:37:36.682Z","updated_at":"2026-04-20T15:05:03.915Z","avatar_url":"https://github.com/agrison.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"Legacy files for Java\n=====================\n\nAre you a Java Developer who needs to parse old times `legacy` flat \nfiles ? You're bored to write each time a parser for these files ? In this\ncase, you've come to the right page. `Legacy4j` is a small Java library that\nhelps you load fixed length files to POJO and saves them the other way.\n\nYou can see the same detailed page on my [website](http://grison.me/me.grison.legacy4j)\n\n\nSo, let's get started.\n\nEnabling The Library\n--------------------\nThat's just a JAR, put it in your classpath, and you're good to go.\n\n\nHello world\n-----------\nLet's say you have this file to read data from.\n\n\tHello     Adam    20100427\n\tBonjour   Robert  20110912\n\tBuongiornoMichele 20041108\n\tGüten tag Karl    20100315\n\nAll you would have to do, is to define your Java object that will\nhold your data, and describe how data fits in it.\n\n```java\n\t@FixedLengthRecord\n\tpublic class Hello {\n\t   @FixedLengthField(10)\n\t   public String greeting;\n\t   @FixedLengthField(8)\n\t   public String name;\n\t   @DateField\n\t   public Calendar meetDate;\n\t   public String toString() { \n\t      return String.format(\"hello[%s, %s, %3$tY-%3$tm-%3$td]\", greeting, name, \n\t                           meetDate); \n\t   }\n\t}\n```\n\nAnd then call the library and ask it gently to read the file data\ninto your object.\n\n```java\n\tpublic class HelloTest {\n\t   public static void main(String[] args) {\n\t      LegacyFile\u003cHello\u003e file = LegacyFiles.openFileReader(\"file.txt\", Hello.class);\n\t      for (Hello hello: file) {\n\t         System.out.println(\"\u003e \" + hello);\n\t      }\n\t      file.close();\n\t   }\n\t}\n```\n\nThat's it, you've read data from your file, with no need of any parser.\n\nHere is the output of the Java program:\n\n\t\u003e hello[Hello     , Adam    , 2010-04-27]\n\t\u003e hello[Bonjour   , Robert  , 2011-09-12]\n\t\u003e hello[Buongiorno, Michele , 2004-11-08]\n\t\u003e hello[Güten tag , Karl    , 2010-03-15]\n\n\n##Field Annotations\n\nThe library makes huge use of Java annotations, that's how it knows how to\nparse the file, and cut the data where it should.\n\nA file is made of records (lines), a Java object needs to have the \n`@FixedLengthRecord` annotation, so that the library knows how to work with it.\n\n\n###FixedLengthField\n\n\nThe `@FixedLengthField` annotation indicates that a field is delimited in size,\nand its size can be set with it.\n\n```java\n\tpublic @interface FixedLengthField {\n\t   /** @return the field length. */\n\t   public int value();\n\t}\n```\n\n###DateField\n\nThe `@DateField` annotation indicates that a field is a Date or a Calendar object,\nand that the data read should be converted to the suitable destination, using a custom \ndate format.\n\n```java\n\tpublic @interface DateField {\n\t   /** @return the date format. */\n\t   public String value() default \"yyyyMMdd\";\n\t}\n```\n\n###DecimalField\n\nThe `@DecimalField` annotation indicates that a field is one of int|Integer, double|Double,\nfloat|Float or BigDecimal and that the data read should be converted to the suitable\ndestination, using a custom decimal format.\n\n```java\n\tpublic @interface DecimalField {\n\t   /** @return the lenth of int and decimal value. */\n\t   public int[] value();\n\t   /** @return the separator if any (must be of length 0 or 1). */\n\t   public String separator() default \"\";\n\t}\n```\n\n###CustomField\n\nThe `@CustomField` annotation indicates that a field is of a custom type and that the data read should be converted to the suitable custom type, using a custom object mapper.\n\n```java\n\tpublic @interface CustomField {\n\t   public Class\u003c?\u003e value();\n\t}\n```\n\n##Field feature annotations\n\n###TrimField\n\nThe `@TrimField` indicates whether a field should be trimmed and in what direction (left, right or both which is the default value).\n\n```java\n\tpublic @interface TrimField {\n\t   public enum Type { Both, Left, Right }\n\n\t   public Type value() defaults Type.Both;\n\t}\n```\n\n###QuoteField\n\nThe `@QuoteField` indicates whether a field should be quoted and with what characters (`[]`, `()`, `{}`, `''` or `\"\"` which is the default value).\n\n```java\n\tpublic @interface QuoteField {\n\t   public enum Type { \n\t      Brackets(\"[\", \"]\"), \n\t      Parenthesis(\"(\", \")\"),\n\t      Braces(\"{\", \"}\"),\n\t      Quote(\"\\\"\", \"\\\"\"),\n\t      SimpleQuote(\"'\", \"'\");\n\n\t      public String before, after;\n\t      Type(String before, after) { this.before = before; this.after = after; }\n\t   }\n\n\t   public Type value() defaults Type.Quote;\n\t}\n```\n\n## Sample code\n\n### The file to be parsed\n\n\t- Awesome report sample data (ignored)\n    # ignored\n    0001[Alex    ]12345678991234567,907820121215Alexandre Grison\n    0002[Foo     ]12345673478751242,901220121223Foo bar bazz\n\n\n                                                Page 1\n\n### The Item entity\n\n```java\n\t// ignore line starting with a # or a - or having only spaces then a Page number (report)\n\t@FixedLengthRecord(ignoreMatching=\"^#.*|^-.*|^\\\\s+Page.*$\", ignoreEmpty=true)\n\tpublic class Item {\n\t   @FixedLengthField(4)\n\t   public int id;\n\t   @FixedLengthField(10)\n\t   @TrimField\n\t   @QuoteField(Type.Brackets)\n\t   public String name;\n\t   @DecimalField({8, 2}) // size = 8 + 2 = 10\n\t   public BigDecimal num1;\n\t   @DecimalField(value={7, 4}, separator=\",\") // size = 7 + 4 + 1 = 12\n\t   public Double num2;\n\t   @DateField(\"yyyyMMdd\") // size = \"yyyyMMdd\".length() = 8\n\t   public Calendar cal;\n\t   @FixedLengthField(22)\n\t   @CustomField(PersonMapper.class)\n\t   public Person person;\n\n\t   public String toString() {\n\t      return String.format(\"Item [id=%s, name='%s', num1=%s, num2=%s, cal=%s, person=%s]\", \n\t            id, name, num1, num2, new SimpleDateFormat(\"dd/MM/yyyy\").format(cal.getTime()), person\n\t      );\n\t   }\n\n\t   public String format() {\n\t      return \"\";\n\t   }\n\t}\n```\n\n### The Person entity\n\n```java\n\tpublic class Person {\n\t   public String firstName, surName;\n\n\t   public String toString() { return String.format(\"p[%s, %s]\", firstName, surName); }\n\t}\n```\n\n### The Person custom mapper\n\n```java\n\tpublic class PersonMapper implements FieldMapper {\n\t   @Override\n\t   public int fill(Object inst, Field field, String value) {\n\t      try {\n\t         FixedLengthField flf = field.getAnnotation(FixedLengthField.class);\n\t         if (flf == null)\n\t            return 0;\n\n\t         if (field.getType() == Person.class) {\n\t            Person p = new Person();\n\t            p.firstName = value.substring(0, 10).trim();\n\t            p.surName = value.substring(10).trim();\n\t            field.set(inst, p);\n\t         }\n\n\t         return flf.value();\n\t      } catch (Throwable e) {\n\t         throw new RuntimeException(e);\n\t      }\n\t   }\n\n\t   @Override\n\t   public String toString(Object inst, Field field) {\n\t      try {\n\t         Person p = (Person)field.get(inst);\n\t         return String.format(\"%-10s%-10s\", p.firstName, p.surName);\n\t      } catch (Throwable e) {\n\t         throw new RuntimeException(e);\n\t      }\n\t   }\n\t}\n```\n\n### The main program\n\n```java\n\tpublic class Test {\n\t   public static void main(String[] args) {\n\t      LegacyFile\u003cItem\u003e file = LegacyFiles.openFileReader(\"positionnal.txt\", Item.class);\n\t      EngineMappers.registerMapper(Person.class, new PersonMapper());\n\t      for (Item item: file) {\n\t         System.out.println(\"\u003e \" + item);\n\t         System.out.println(\"= \" + RecordConverter.toString(item));\n\t      }\n\t      file.close();\n\t   }\n\t}\n```\n\n### The result\n\n    \u003e Item [id=1, name='Alex', num1=12345678.99, num2=1234567.9078, cal=15/12/2012, person=p[Alexandre, Grison]]\n    = 0001[Alex    ]12345678991234567,907820121215Alexandre Grison\n    \u003e Item [id=2, name='Foo', num1=12345673.47, num2=8751242.9012, cal=23/12/2012, person=p[Foo bar ba, zz]]\n    = 0002[Foo     ]12345673478751242,901220121223Foo bar bazz\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagrison%2Flegacy4j","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagrison%2Flegacy4j","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagrison%2Flegacy4j/lists"}