{"id":18691050,"url":"https://github.com/ffissore/jrecordbind","last_synced_at":"2025-06-16T08:04:56.976Z","repository":{"id":5376576,"uuid":"6563886","full_name":"ffissore/jrecordbind","owner":"ffissore","description":"Tiny and super fast fixed-length files reader/parser","archived":false,"fork":false,"pushed_at":"2023-02-01T04:11:11.000Z","size":484,"stargazers_count":32,"open_issues_count":11,"forks_count":18,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-12T06:18:07.466Z","etag":null,"topics":["fixed-length-records","marshal","parser","unmarshall","xml-schema"],"latest_commit_sha":null,"homepage":"https://jrecordbind.org","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ffissore.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-11-06T15:02:10.000Z","updated_at":"2024-09-09T07:47:01.000Z","dependencies_parsed_at":"2023-02-17T02:01:38.946Z","dependency_job_id":null,"html_url":"https://github.com/ffissore/jrecordbind","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ffissore/jrecordbind","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ffissore%2Fjrecordbind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ffissore%2Fjrecordbind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ffissore%2Fjrecordbind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ffissore%2Fjrecordbind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ffissore","download_url":"https://codeload.github.com/ffissore/jrecordbind/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ffissore%2Fjrecordbind/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260123995,"owners_count":22962198,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["fixed-length-records","marshal","parser","unmarshall","xml-schema"],"created_at":"2024-11-07T10:53:03.235Z","updated_at":"2025-06-16T08:04:56.930Z","avatar_url":"https://github.com/ffissore.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/ffissore/jrecordbind.svg?branch=master)](https://travis-ci.org/ffissore/jrecordbind)\n![License](https://img.shields.io/github/license/ffissore/jrecordbind.svg)\n![Version](https://img.shields.io/maven-central/v/org.fissore.jrecordbind/jrecordbind.svg)\n[![security status](https://www.meterian.com/badge/gh/ffissore/jrecordbind/security)](https://www.meterian.com/report/gh/ffissore/jrecordbind)\n[![stability status](https://www.meterian.com/badge/gh/ffissore/jrecordbind/stability)](https://www.meterian.com/report/gh/ffissore/jrecordbind)\n\n# What's JRecordBind?\n\nA tiny and super fast library that aims to:\n\n- parse fixed or variable length text files and map them to java beans (`Unmarshaller`)\n- export java beans to fixed or variable length text files (`Marshaller`)\n\n## Why?\n\nAlmost everybody has written an import procedure of some sort.\n\nFixed-length are a must for every public institution (at least in Italy): regardless of the age of the destination system, everyone can read a plain text file.\n\nJRecordBind aims to leverage the boring parsing task and let the developer focus on the real problem: understanding the data and find a way to feed the persistence layer.\n\n## What's a record?\n\nA record is a structured text, a line usually, which contains typed information. For example:\n\n```\nJOHN                SMITH               ABCDEF88L99H123B1979051800000000811804                  197Y\n```\n\nThis record starts with a \"name\" property from column 0 to column 20, right padded with spaces. Then it has a \"surname\" property, equally padded, from 21 to 40.\n\nSecond to last property is a float property from column 89 to 99, left padded with spaces, where the last 2 digits are the decimal part: its value is 1.97\n\nIt ends with a boolean property, where \"Y\" means \"true\" and \"N\" means \"false\".\n\n## Advantages\n\nJRecordBind is (AFAIK) the only tool aimed at fixed-length files that's able to **marshall (write)** and **unmarshall (read)**. By the way you may be a producer of fixed-length files, not just a consumer.\n\nJRecordBind supports **hierarchical** fixed-length files: records of some type that are used only after parent record types.\n\nJRecordBind uses **XML Schema** to define the file format: that could make JRecordBind quickier to learn.\n\n## Which Java?\n\nSince version 3.0.0, JRecordBind requires Java 11+.\n\nYour application `module-info.java` file needs to require `org.fissore.jrecordbind` and `java.xml.bind`, and to export generated classes to JRecordBind, like so:\n\n```java\nmodule com.mycompany {\n\n  requires org.fissore.jrecordbind;\n  requires java.xml.bind;\n\n  exports com.mycompany.generated to org.fissore.jrecordbind;\n\n}\n```\n\n## Maven\n\nMaven users can add JRecordBind as a dependency\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.fissore.jrecordbind\u003c/groupId\u003e\n  \u003cartifactId\u003ejrecordbind\u003c/artifactId\u003e\n  \u003cversion\u003e3.3.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Support\n\nIf you need support, [drop me an email](mailto:federico@fissore.org). If you have found a bug, [please report it! report it now!](https://github.com/ffissore/jrecordbind/issues)\n\n## Is it good?\n\nYes, it is.\n\n## Show me!\n\nTake a look at the [example project](https://github.com/ffissore/jrecordbind/tree/3.3.0/jrecordbind-example), read the how-tos below, and play with the [tests](https://github.com/ffissore/jrecordbind/tree/3.3.0/jrecordbind-test/src/test/resources/record_definitions).\n\n## How does it work?\n\n### Record definition\n\nWhen importing a fixed-length file, someone has provided a thorough documentation regarding how the file is structured: each property, their length, their value and how to convert them.\n\nJRecordBind needs that documentation: it's the starting point. Your task is to write an XML Schema version of it. Here's an example:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cxs:schema targetNamespace=\"http://schemas.jrecordbind.org/jrb/simple\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.jrecordbind.org/jrb/simple\" xmlns:jrb=\"http://jrecordbind.org/3/xsd\" elementFormDefault=\"qualified\"\u003e\n  \u003cxs:complexType name=\"SimpleRecord\"\u003e\n    \u003cxs:sequence\u003e\n      \u003cxs:element name=\"name\" type=\"xs:string\" jrb:length=\"20\"/\u003e\n      \u003cxs:element name=\"surname\" type=\"xs:string\" jrb:length=\"20\"/\u003e\n      \u003cxs:element name=\"taxCode\" type=\"xs:string\" jrb:length=\"16\"/\u003e\n      \u003cxs:element name=\"birthday\" type=\"xs:date\" jrb:length=\"8\" jrb:converter=\"org.fissore.jrecordbindtests.test.TestConverters$SimpleRecordDateConverter\"/\u003e\n      \u003cxs:element name=\"oneInteger\" type=\"xs:int\" jrb:length=\"10\" jrb:padder=\"org.fissore.jrecordbind.padders.ZeroLeftPadder\"/\u003e\n      \u003cxs:element name=\"twoInteger\" type=\"xs:int\" jrb:length=\"15\" jrb:padder=\"org.fissore.jrecordbind.padders.SpaceRightPadder\"/\u003e\n      \u003cxs:element name=\"oneFloat\" type=\"xs:float\" jrb:length=\"10\" jrb:converter=\"org.fissore.jrecordbindtests.test.TestConverters$SimpleRecordFloatConverter\" jrb:padder=\"org.fissore.jrecordbind.padders.SpaceLeftPadder\"/\u003e\n      \u003cxs:element name=\"selected\" type=\"xs:boolean\" jrb:length=\"1\" jrb:converter=\"org.fissore.jrecordbindtests.test.TestConverters$YNBooleanConverter\"/\u003e\n    \u003c/xs:sequence\u003e\n  \u003c/xs:complexType\u003e\n  \u003cxs:element name=\"main\" type=\"SimpleRecord\" jrb:length=\"120\"/\u003e\n\u003c/xs:schema\u003e\n```\n\nIt's a standard XML Schema file (xsd) plus some special **jrb** attributes and a mandatory **main** element.\n\nThe **main** element will be the starting point, the main bean JRecordBean will read/write.\n\nHere's the list of **jrb** attributes:\n\n\u003ctable border=\"1\"\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\n    \u003cstrong\u003eATTRIBUTE\u003c/strong\u003e\n  \u003c/td\u003e\n  \u003ctd\u003e\n    \u003cstrong\u003eSCOPE\u003c/strong\u003e\n  \u003c/td\u003e\n  \u003ctd\u003e\n    \u003cstrong\u003eMEANING\u003c/strong\u003e\n  \u003c/td\u003e\n  \u003ctd\u003e\n    \u003cstrong\u003eMANDATORY\u003c/strong\u003e\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:length\u003c/td\u003e\n  \u003ctd\u003emain element\u003c/td\u003e\n  \u003ctd\u003eThe length of the fixed-length file\u003c/td\u003e\n  \u003ctd\u003eYes, unless \"delimiter\" is specified. When \"length\" is specified, it's a fixed-length file. When \"length\" is not specified, it's a dynamic-length file\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:length\u003c/td\u003e\n  \u003ctd\u003eother elements\u003c/td\u003e\n  \u003ctd\u003eThe length of that particular property\u003c/td\u003e\n  \u003ctd\u003eYes, unless the file is a dynamic-length file\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:delimiter\u003c/td\u003e\n  \u003ctd\u003emain element\u003c/td\u003e\n  \u003ctd\u003eWhat delimits each property\u003c/td\u003e\n  \u003ctd\u003eNo, unless it's dynamic-length file\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:padder\u003c/td\u003e\n  \u003ctd\u003emain element\u003c/td\u003e\n  \u003ctd\u003eThe default padder to use when not otherwise specified on elements (see below)\u003c/td\u003e\n  \u003ctd\u003eNo. JRecordBind will use its default (right padding with spaces)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:padder\u003c/td\u003e\n  \u003ctd\u003eother elements\u003c/td\u003e\n  \u003ctd\u003eA custom padder for that property\u003c/td\u003e\n  \u003ctd\u003eNo. JRecordBind will use the default one (see above)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:lineSeparator\u003c/td\u003e\n  \u003ctd\u003emain element\u003c/td\u003e\n  \u003ctd\u003eWhat ends each line in the file\u003c/td\u003e\n  \u003ctd\u003eNo. By default a \"new line\" char will be used. DOS format files can be achieved using the value \"\u0026amp;#13;\u0026amp;#10;\"\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:converter\u003c/td\u003e\n  \u003ctd\u003eother elements\u003c/td\u003e\n  \u003ctd\u003eHow to convert that field to/from a string. Elements with types xs:string, xs:int, and xs:long are automatically converted.\u003c/td\u003e\n  \u003ctd\u003eNo. JRecordBind will treat the property as a string\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:row\u003c/td\u003e\n  \u003ctd\u003eother elements\u003c/td\u003e\n  \u003ctd\u003eIf a record is split into more than one line, from the second line on, specify the line number (zero based)\u003c/td\u003e\n  \u003ctd\u003eNo. JRecordBind will default it to 0: the whole record on one line. See the multi-row.def.xsd example\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:subclass\u003c/td\u003e\n  \u003ctd\u003exs:complexType tag\u003c/td\u003e\n  \u003ctd\u003eIf you need to extend/override some generated class, you can make JRecordBind instantiate the specified class instead of the generated one. The specified class must extend the generated class. See the enum.def.xsd example\u003c/td\u003e\n  \u003ctd\u003eNo. JRecordBind will default to the generated class\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003ejrb:setter\u003c/td\u003e\n  \u003ctd\u003exs:choice declaration\u003c/td\u003e\n  \u003ctd\u003eWhen using JAXB bindings.xjb with the \u003cstrong\u003e\"globalBindings choiceContentProperty='true'\"\u003c/strong\u003e, specify the name of the method JAXB has generated\u003c/td\u003e\n  \u003ctd\u003eYes, if using choiceContentProperty='true' in bindings.xjb\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nWhen the definition is ready, generate the beans: use [JAXB2 Maven plugin](https://github.com/highsource/maven-jaxb2-plugin) (for an example configuration, give a look at the [test pom.xml](https://github.com/ffissore/jrecordbind/blob/3.3.0/jrecordbind-test/pom.xml)).\n\nCongratulations! You are now ready to read/write fixed-length files.\n\n### A note on dates\n\nWhen an element is of type `xs:date`, by default JAXB will generate a class field of type `XMLGregorianCalendar`, which JRecordBind does not support. It's mandatory to use a minimal `bindings.xjb` file in order to make JAXB generate `Calendar` fields, like this one:\n\n```xml\n\u003cbindings xmlns=\"http://java.sun.com/xml/ns/jaxb\" version=\"2.0\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\u003e\n  \u003cglobalBindings\u003e\n    \u003cjavaType name=\"java.util.Calendar\" xmlType=\"xs:date\" parseMethod=\"javax.xml.bind.DatatypeConverter.parseDate\" printMethod=\"javax.xml.bind.DatatypeConverter.printDate\"/\u003e\n    \u003cjavaType name=\"java.util.Calendar\" xmlType=\"xs:dateTime\" parseMethod=\"javax.xml.bind.DatatypeConverter.parseDate\" printMethod=\"javax.xml.bind.DatatypeConverter.printDate\"/\u003e\n  \u003c/globalBindings\u003e\n\u003c/bindings\u003e\n```\n\nTake a look at the [example project](https://github.com/ffissore/jrecordbind/tree/3.3.0/jrecordbind-example).\n\n### Read/Unmarshall\n\nGiven an fixed-length text file, honoring the definition, for example\n\n```\nWALTER              LIPPMANN            ABCDEF79D18K999A1889092381197\nDAVID               JOHNSON             ABCDEF79E18S999B1889092381197\n```\n\nyou can read/unmarshall it this way:\n\n```java\nUnmarshaller\u003cSimpleRecord\u003e unmarshaller = new Unmarshaller\u003cSimpleRecord\u003e(\n    new InputStreamReader(getClass().getResourceAsStream(\"/simple.def.xsd\")));\n\nIterator\u003cSimpleRecord\u003e iter = unmarshaller.unmarshall(\n    new InputStreamReader(getClass().getResourceAsStream(\"simple_test.txt\")));\n\nassertTrue(iter.hasNext());\n\nSimpleRecord record = iter.next();\nassertEquals(\"WALTER\", record.getName());\nassertEquals(\"LIPPMANN\", record.getSurname());\nassertEquals(\"ABCDEF79D18K999A\", record.getTaxCode());\n```\n\n### Write/Marshall\n\nGiven a bean loaded with data, you can write/marshall it this way:\n\n```java\nSimpleRecord record = new SimpleRecord();\nrecord.setName(\"WALTER\");\nrecord.setSurname(\"LIPPMANN\");\n// other properties omitted\n\nMarshaller\u003cSimpleRecord\u003e marshaller = new Marshaller\u003cSimpleRecord\u003e(\n    new InputStreamReader(getClass().getResourceAsStream(\"/simple.def.xsd\")));\n\nWriter writer = new StringWriter();\n\nmarshaller.marshall(record, writer);\n\nSystem.out.println(writer.toString());\n```\n\n## How-tos\n\n### Different record types\n\nHierarchical fixed-length files use ID fields to distinguish the records. Documentation will say something like \"Record 000 is an address, records A01 are vehicles...\" and so on.\n\nJRecordBind can easily recognize each record type using the XML Schema standard attribute `fixed`: [see this example](https://github.com/ffissore/jrecordbind/blob/3.3.0/jrecordbind-test/src/test/resources/record_definitions/hierarchical.def.xsd).\n\n### Dynamic length files\n\nYou can omit the `jrb:length` attribute and instead specify the `jrb:delimiter` attribute: this way you get a **dynamic-length** file: [see this example](https://github.com/ffissore/jrecordbind/blob/3.3.0/jrecordbind-test/src/test/resources/record_definitions/dynamic_length.def.xsd).\n\n### Extend the generated bean\n\nAdd the `jrb:subclass` attribute to the `xs:complexType` tag. By specifying the fully qualified name of a class extending the generated class, JRecordBind will instantiate that class instead of the generated one, allowing you to extend/override the generated class: [see this example](https://github.com/ffissore/jrecordbind/blob/3.3.0/jrecordbind-test/src/test/resources/record_definitions/enum.def.xsd).\n\n### Using xs:choice with choiceContentProperty='true'\n\nAdd the `jrb:setter` attribute to the `xs:choice` tag.\n\nWith the types `One` and `Two` inside an `xs:choice` element, the default generated class will have methods `setOne` and `setTwo`. JAXB can generate only one method by specifying `choiceContentProperty=true` in file `bindings.xjb`: the generated method will be `setOneOrTwo`.\n\nJRecordBind is not aware of this JAXB trick, it will look for methods `setOne` and `setTwo` and it will fail: that information must be duplicated into the XML Schema, in a way JRecordBind can understand.\n\nAdd the attribute `jrb:setter=\"oneOrTwo\"` to the `xs:choice` tag, and JRecordBind will work as expected.\n\n[See this example](https://github.com/ffissore/jrecordbind/blob/3.3.0/jrecordbind-test/src/test/resources/record_definitions/choice.def.xsd#L24).\n\n### Fine grained control on file reading when unmarshalling\n\nWhen the `Unmarshaller` reads from the file, by default it returns the current line.\n\nIn order to customize such behaviour, create an `Unmarshaller` passing an implementation of the `LineReader` interface: [see this example](https://github.com/ffissore/jrecordbind/blob/3.3.0/jrecordbind-test/src/test/java/org/fissore/jrecordbindtests/test/SimpleNotPaddedLineReaderUnmarshallTest.java).\n\n### Use a custom line separator (and reading/writing DOS format files)\n\nLine separator can be customized using the attribute `jrb:lineSeparator`. By default, lines will be separated by a \"new line\" char (`\\n`). If order to read/write DOS format files, specify the attribute this way\n\n```\njrb:lineSeparator=\"\u0026#13;\u0026#10;\"\n```\n\nwhich is the XML equivalent of `\\r\\n`.\n\n### Other examples\n\nEach feature of JRecordBind has at least one xsd file that tests it.\n\nTake a look at the [repository](https://github.com/ffissore/jrecordbind/tree/3.3.0/jrecordbind-test/src/test/resources/record_definitions).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fffissore%2Fjrecordbind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fffissore%2Fjrecordbind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fffissore%2Fjrecordbind/lists"}