{"id":15045019,"url":"https://github.com/ozlerhakan/poiji","last_synced_at":"2026-01-02T13:24:43.662Z","repository":{"id":16147187,"uuid":"79450687","full_name":"ozlerhakan/poiji","owner":"ozlerhakan","description":":candy: A library converting XLS and XLSX files to a list of Java objects based on Apache POI","archived":false,"fork":false,"pushed_at":"2025-01-19T11:12:39.000Z","size":9156,"stargazers_count":474,"open_issues_count":5,"forks_count":135,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-03-29T09:05:21.597Z","etag":null,"topics":["apache","apache-poi","converter","data","deserialize","excel","java","java-11","mapper","mapping","microsoft-excel","parser","performance","poi","poiji","pojo","unmarshall"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ozlerhakan.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"ozlerhakan"}},"created_at":"2017-01-19T12:23:12.000Z","updated_at":"2025-03-19T16:49:03.000Z","dependencies_parsed_at":"2023-01-13T20:30:34.005Z","dependency_job_id":"847d4c90-e69b-4b6a-87ce-2e3c6657b764","html_url":"https://github.com/ozlerhakan/poiji","commit_stats":{"total_commits":416,"total_committers":33,"mean_commits":"12.606060606060606","dds":"0.23317307692307687","last_synced_commit":"835db2d34b18f05cb63bb23f3534b888d561c297"},"previous_names":[],"tags_count":59,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozlerhakan%2Fpoiji","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozlerhakan%2Fpoiji/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozlerhakan%2Fpoiji/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozlerhakan%2Fpoiji/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ozlerhakan","download_url":"https://codeload.github.com/ozlerhakan/poiji/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247318740,"owners_count":20919484,"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":["apache","apache-poi","converter","data","deserialize","excel","java","java-11","mapper","mapping","microsoft-excel","parser","performance","poi","poiji","pojo","unmarshall"],"created_at":"2024-09-24T20:51:21.327Z","updated_at":"2026-01-02T13:24:43.656Z","avatar_url":"https://github.com/ozlerhakan.png","language":"Java","funding_links":["https://github.com/sponsors/ozlerhakan"],"categories":[],"sub_categories":[],"readme":":toc: macro\n:toclevels: 2\n\n= Poiji\n:version: v5.2.0\n:branch: 5.2.0\n\nimage:https://github.com/ozlerhakan/poiji/actions/workflows/maven.yml/badge.svg[\"Build Status\"] image:https://app.codacy.com/project/badge/Grade/64f7e2cb9e604807b62334a4cfc3952d[\"Codacy code quality\",link=\"https://www.codacy.com/gh/ozlerhakan/poiji/dashboard?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=ozlerhakan/poiji\u0026utm_campaign=Badge_Grade\"]\nimage:https://codecov.io/gh/ozlerhakan/poiji/branch/master/graph/badge.svg?token=MN6V6xOWBq[\"Codecov\",link=\"https://codecov.io/gh/ozlerhakan/poiji\"] image:https://img.shields.io/badge/apache.poi-5.4.1-brightgreen.svg[] image:https://app.fossa.com/api/projects/git%2Bgithub.com%2Fozlerhakan%2Fpoiji.svg?type=shield[\"FOSSA Status\",link=\"https://app.fossa.com/projects/git%2Bgithub.com%2Fozlerhakan%2Fpoiji?ref=badge_shield\"]\n\nPoiji is a tiny thread-safe Java library that provides one way mapping from Excel sheets to Java classes.\nIn a way it lets us convert each row of the specified excel data into Java objects.\nPoiji uses https://poi.apache.org/[Apache Poi] (the Java API for Microsoft Documents) under the hood to fulfill the mapping process.\n\n[%collapsible]\ntoc::[]\n\n== Getting Started\n\nIn your Maven/Gradle project, first add the corresponding dependency:\n\n.maven\n[source,xml]\n----\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.ozlerhakan\u003c/groupId\u003e\n  \u003cartifactId\u003epoiji\u003c/artifactId\u003e\n  \u003cversion\u003e5.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n----\n\nYou can find the latest and earlier development versions including javadoc and source files at https://central.sonatype.com/artifact/com.github.ozlerhakan/poiji.\n\n== Usage\n\n.`Poiji.fromExcel` Structure\n----\ncom.poiji.bind.Poiji#fromExcel(java.io.File, java.lang.Class\u003cT\u003e)\ncom.poiji.bind.Poiji#fromExcel(java.io.File, java.lang.Class\u003cT\u003e, java.util.function.Consumer\u003c? super T\u003e)\ncom.poiji.bind.Poiji#fromExcel(java.io.File, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions)\ncom.poiji.bind.Poiji#fromExcel(java.io.File, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions, java.util.function.Consumer\u003c? super T\u003e)\ncom.poiji.bind.Poiji#fromExcel(java.io.InputStream, com.poiji.exception.PoijiExcelType, java.lang.Class\u003cT\u003e)\ncom.poiji.bind.Poiji#fromExcel(java.io.InputStream, com.poiji.exception.PoijiExcelType, java.lang.Class\u003cT\u003e, java.util.function.Consumer\u003c? super T\u003e)\ncom.poiji.bind.Poiji#fromExcel(java.io.InputStream, com.poiji.exception.PoijiExcelType, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions)\ncom.poiji.bind.Poiji#fromExcel(java.io.InputStream, com.poiji.exception.PoijiExcelType, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions, java.util.function.Consumer\u003c? super T\u003e)\ncom.poiji.bind.Poiji#fromExcel(org.apache.poi.ss.usermodel.Sheet, java.lang.Class\u003cT\u003e)\ncom.poiji.bind.Poiji#fromExcel(org.apache.poi.ss.usermodel.Sheet, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions)\ncom.poiji.bind.Poiji#fromExcel(org.apache.poi.ss.usermodel.Sheet, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions, java.util.function.Consumer\u003c? super T\u003e)\n\ncom.poiji.bind.Poiji#fromExcelProperties(java.io.File, java.lang.Class\u003cT\u003e)\ncom.poiji.bind.Poiji#fromExcelProperties(java.io.File, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions)\ncom.poiji.bind.Poiji#fromExcelProperties(java.io.InputStream, com.poiji.exception.PoijiExcelType, java.lang.Class\u003cT\u003e)\ncom.poiji.bind.Poiji#fromExcelProperties(java.io.InputStream, com.poiji.exception.PoijiExcelType, java.lang.Class\u003cT\u003e, com.poiji.option.PoijiOptions)\n----\n\n.`PoijiOptions.PoijiOptionsBuilder` Structure\n----\ncom.poiji.option.PoijiOptions.PoijiOptionsBuilder\n    #settings()\n    #build()\n    #dateLenient(boolean)\n    #dateRegex(String)\n    #timeRegex(String)\n    #dateTimeRegex(String)\n    #datePattern(String)\n    #dateFormatter(java.time.format.DateTimeFormatter)\n    #timeFormatter(java.time.format.DateTimeFormatter)\n    #dateTimeFormatter(java.time.format.DateTimeFormatter)\n    #ignoreHiddenSheets(boolean)\n    #password(String)\n    #preferNullOverDefault(boolean)\n    #settings(int)\n    #sheetIndex(int)\n    #sheetName(String)\n    #skip(int)\n    #limit(int)\n    #trimCellValue(boolean)\n    #headerStart(int)\n    #headerCount(int)\n    #withCasting(Casting)\n    #withFormatting(Formatting)\n    #caseInsensitive(boolean)\n    #ignoreWhitespaces(boolean)\n    #poijiNumberFormat(PoijiNumberFormat)\n    #poijiLogCellFormat(PoijiLogCellFormat)\n    #disableXLSXNumberCellFormat()\n    #addListDelimiter(String)\n    #setLocale(java.util.Locale)\n    #rawData(boolean)\n    #setIgnoreFileExtension(boolean)\n----\n\n== Documentation\n\nHere are the list of features with examples that the latest version of Poiji supports.\n\n=== Annotations\n\nCreate your object model:\n\n[source,java]\n----\npublic class Employee {\n\n    @ExcelRow                    \u003c1\u003e\n    private int rowIndex;\n\n    @ExcelCell(0)                \u003c2\u003e\n    private long employeeId;     \u003c3\u003e\n\n    @ExcelCell(1)\n    private String name;\n\n    @ExcelCell(2)\n    private String surname;\n\n    @ExcelCell(3)\n    private int age;\n\n    @ExcelCell(4)\n    private boolean single;\n\n    @ExcelCellName(\"emails\")     \u003c4\u003e\n    List\u003cString\u003e emails;\n\n    @ExcelCell(5)\n    List\u003cBigDecimal\u003e bills;\n\n    //no need getters/setters to map excel cells to fields\n}\n----\n\u003c1\u003e Optionally, we can access the index of each row item by using the `ExcelRow` annotation. Annotated variable should be of type `int`, `double`, `float` or `long`.\n\u003c2\u003e A field must be annotated with `@ExcelCell` along with its property in order to get the value from the right coordinate in the target excel sheet.\n\u003c3\u003e An annotated field can be either protected, private or public modifier. The field may be either of `boolean`, `int`, `long`, `float`, `double`, or their wrapper classes. You can add a field of `java.util.Date`, `java.time.LocalDate`, `java.time.LocalTime`, `java.time.LocalDateTime` and `String` as well.\n\u003c4\u003e If one column contains multiple value, you can get them using a List field. A List field can store items which is of type `BigDecimal`, `Long`, `Double`, `Float`, `Integer`, `Boolean` and `String`.\n\nThis is the excel file (`employees.xlsx`) we want to map to a list of `Employee` instance:\n\n|===\n|ID | NAME |SURNAME |AGE |SINGLE |BILLS | EMAILS\n\n|123923\n|Joe\n|Doe\n|30\n|TRUE\n|123,10;99.99\n|joe@doe.com;joedoe@gmail.com\n\n|123123\n|Sophie\n|Derue\n|20\n|TRUE\n|1022\n|sophie.derue@gmail.com;sophie@derue.com\n\n|135923\n|Paul\n|Raul\n|31\n|FALSE\n|73,25;70\n|paul.raul@gmail.com;paul@raul.com\n|===\n\nThe snippet below shows how to obtain the excel data using `Poiji`.\n\n[source,java]\n----\nPoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()\n        .addListDelimiter(\";\") \u003c1\u003e\n        .build();\nList\u003cEmployee\u003e employees = Poiji.fromExcel(new File(\"employees.xls\"), Employee.class, options);\n// alternatively\nInputStream stream = new FileInputStream(new File(\"employees.xls\"))\nList\u003cEmployee\u003e employees = Poiji.fromExcel(stream, PoijiExcelType.XLS, Employee.class, options);\n\nemployees.size();\n// 3\nEmployee firstEmployee = employees.get(0);\n// Employee{rowIndex=1, employeeId=123923, name='Joe', surname='Doe', age=30, single=true, emails=[joe@doe.com, joedoe@gmail.com], biils=[123,10, 99.99]}\n----\n\u003c1\u003e By default the delimiter/separator is `,` to split items in a cell. There is an option to change this behavior. Since we use `;` between items, we need to tell Poiji to use `;` as a separator.\n\nBy default, Poiji ignores the header row of the excel data. If you want to ignore the first row of data, you need to use `PoijiOptions`.\n\n[source,java]\n----\nPoijiOptions options = PoijiOptionsBuilder.settings(1).build(); // we eliminate Joe Doe.\nList\u003cEmployee\u003e employees = Poiji.fromExcel(new File(\"employees.xls\"), Employee.class, options);\nEmployee firstEmployee = employees.get(0);\n// Employee{rowIndex=2, employeeId=123123, name='Sophie', surname='Derue', age=20, single=true, emails=[sophie.derue@gmail.com, sophie@derue.com], biils=[1022]}\n----\n\nBy default, Poiji selects the first sheet of an excel file. You can override this behaviour like below:\n\n[source,java]\n----\nPoijiOptions options = PoijiOptionsBuilder.settings()\n                       .sheetIndex(1) \u003c1\u003e\n                       .build();\n----\n1. Poiji should look at the second (zero-based index) sheet of your excel file.\n== Documentation\n\n=== Java Records Support\n\nStarting with version 5.0.0, Poiji supports Java Records (Java 16+). Records provide a concise way to declare data carrier classes with immutable fields.\n\n**Compatibility Note**: The library maintains Java 11 compatibility for regular classes. Record support is automatically available when running on Java 17+ runtime, but the library will work on Java 11 for traditional POJOs.\n\n[source,java]\n----\npublic record EmployeeRecord(\n    @ExcelCellName(\"ID\")\n    long employeeId,\n\n    @ExcelCellName(\"NAME\")\n    String name,\n\n    @ExcelCellName(\"SURNAME\")\n    String surname,\n\n    @ExcelCellName(\"AGE\")\n    int age,\n\n    @ExcelCellName(\"SINGLE\")\n    boolean single,\n\n    @ExcelRow\n    int row\n) {}\n----\n\nUse records just like regular classes with Poiji:\n\n[source,java]\n----\nList\u003cEmployeeRecord\u003e employees = Poiji.fromExcel(\n    new File(\"employees.xlsx\"),\n    EmployeeRecord.class\n);\n\nEmployeeRecord firstEmployee = employees.get(0);\n// Access fields using record accessor methods\nSystem.out.println(firstEmployee.name());\nSystem.out.println(firstEmployee.age());\n----\n\nRecords work with all annotations including:\n\n* `@ExcelCell` - Map by column index\n* `@ExcelCellName` - Map by column name\n* `@ExcelRow` - Capture row number\n* `@ExcelUnknownCells` - Capture unmapped cells\n\nRecords work with both XLS and XLSX formats and support all standard Poiji options.\n\n**Requirements**:\n\n* Library compiled with Java 11 (maintains backward compatibility)\n* Record support requires Java 17+ runtime\n* Traditional POJOs work on any Java 11+ runtime\n\n=== Ignore File Extension\n\nIgnore file extension validation. When `PoijiOptions.ignoreFileExtension` set to true, Poiji will not throw `InvalidExcelFileExtension` if the file extension is not `.xls` or `.xlsx`. This is useful when working with temporary files that don't have proper extensions.\n\nNote: Files will be treated as XLSX format when the extension is ignored. XLS files without proper extensions are not supported with this option.\n\n=== Prefer Default Value\n\nIf you want a date field to return `null` rather than a default date, use `PoijiOptionsBuilder` with the `preferNullOverDefault` method as follows:\n\n[source,java]\n----\nPoijiOptions options = PoijiOptionsBuilder.settings()\n                       .preferNullOverDefault(true) \u003c1\u003e\n                       .build();\n----\n1. a field that is of type either `java.util.Date`, `Float`, `Double`, `Integer`, `Long` or `String` will have a `null` value.\n\n=== Sheet Name Option\n\nPoiji allows specifying the sheet name using annotation\n\n[source,java]\n----\n@ExcelSheet(\"Sheet2\")  (1)\npublic class Student {\n\n    @ExcelCell(0)\n    private String name;\n\n    @ExcelCell(1)\n    private String id;\n\n    @ExcelCell(2)\n    private String phone;\n\n\n    @Override\n    public String toString() {\n        return \"Student {\" +\n                \" name=\" + name +\n                \", id=\" + id + \"'\" +\n                \", phone='\" + phone + \"'\" +\n                '}';\n    }\n}\n----\n\u003c1\u003e With the `ExcelSheet` annotation we are configuring the name of the sheet to read data from. The other sheets will be ignored.\n\n=== Protected Excels\n\nConsider that your excel file is protected with a password, you can define the password via `PoijiOptionsBuilder` to read rows:\n\n----\nPoijiOptions options = PoijiOptionsBuilder.settings()\n                    .password(\"1234\")\n                    .build();\nList\u003cEmployee\u003e employees = Poiji.fromExcel(new File(\"employees.xls\"), Employee.class, options);\n----\n\n=== Annotation ExcelCellName\n\nUsing `ExcelCellName`, we can read the values by column names directly.\n\n[source,java]\n----\npublic class Person {\n\n    @ExcelCellName(\"Name\")  \u003c1\u003e\n    protected String name;\n\n    @ExcelCellName(\"Address\")\n    protected String address;\n\n    @ExcelCellName(\"Age\")\n    protected int age;\n\n    @ExcelCellName(\"Email\")\n    protected String email;\n\n    @ExcelCellName(value = \"\", expression = \"Surname|Second name\")  \u003c2\u003e\n    private String surname;\n\n}\n----\n1. We need to specify the `name` of the column for which the corresponding value is looked. By default, `@ExcelCellName` is case-sensitive and the excel file should't contain duplicated column names. However, you can manipulate this feature using `PoijiOptionsBuilder#caseInsensitive(boolean)` and you can ignore white spaces using `PoijiOptionsBuilder#ignoreWhitespaces(boolean)`.\n2. In rare situations a column can have synonyms, especially when the column was renamed and backward compatibility is needed.\nHere we specify the `expression` leaving the column `name` empty.\n\nFor example, here is the excel (`person.xls`) file we want to use:\n\n|===\n| Name |Address |Age |Email |Surname\n\n|Joe\n|San Francisco, CA\n|30\n|joe@doe.com\n|Doe\n\n|Sophie\n|Costa Mesa, CA\n|20\n|sophie@doe.com\n|Doe\n\n|===\n\n[source,java]\n----\nList\u003cPerson\u003e people = Poiji.fromExcel(new File(\"person.xls\"), Person.class);\npeople.size();\n// 2\nPerson person = people.get(0);\n// Joe\n// San Francisco, CA\n// 30\n// joe@doe.com\n// Doe\n----\n\nGiven that the first column always stands for the names of people, you're able to combine the `ExcelCell` annotation with `ExcelCellName` in your object model:\n\n[source,java]\n----\npublic class Person {\n\n    @ExcelCell(0)\n    protected String name;\n\n    @ExcelCellName(\"Address\")\n    protected String address;\n\n    @ExcelCellName(\"Age\")\n    protected int age;\n\n    @ExcelCellName(\"Email\")\n    protected String email;\n\n}\n----\n\n=== Super Class Inheritance\n\nYour object model may be derived from a super class:\n\n[source,java]\n----\npublic abstract class Vehicle {\n\n    @ExcelCell(0)\n    protected String name;\n\n    @ExcelCell(1)\n    protected int year;\n}\n\npublic class Car extends Vehicle {\n\n    @ExcelCell(2)\n    private int nOfSeats;\n}\n----\n\nand you want to map the table (`car.xlsx`) below to Car objects:\n\n|===\n|NAME |YEAR |SEATS\n\n|Honda Civic\n|2017\n|4\n\n|Chevrolet Corvette\n|2017\n|2\n|===\n\nUsing Poiji, you can map the annotated field(s) of super class(es) of the target class like so:\n\n[source,java]\n----\nList\u003cCar\u003e cars = Poiji.fromExcel(new File(\"cars.xls\"), Car.class);\ncars.size();\n// 2\nCar car = cars.get(0);\n// Honda Civic\n// 2017\n// 4\n----\n\n=== Annotation ExcelCellsJoinedByName\n\nUsing `ExcelCellsJoinedByName` we can read columns which name meets same regular expression. Values will be combined as a multi valued map.\n\nPlease pay attention the variable must be initialized explicitly.\n\n[source,java]\n----\npublic class Album {\n\n    @ExcelCellsJoinedByName(expression = \"Artist\")  \u003c1\u003e\n    private MultiValuedMap\u003cString, String\u003e artists = new ArrayListValuedHashMap\u003c\u003e();\n\n    @ExcelCellsJoinedByName(expression = \"Track[0-9]+\")  \u003c2\u003e\n    private MultiValuedMap\u003cString, String\u003e tracks = new ArrayListValuedHashMap\u003c\u003e();\n\n}\n----\n1. Here we map multiple columns with `name` _Artist_.\n2. Here we map multiple columns with `name` _Track1_, _Track2_, _Track3_, etc.\n\nFor example, here is the excel (`album.xls`) file we want to use:\n\n|===\n| Artist |Artist |Artist |Track1 |Track2\n\n|Michael Jackson\n|Lionel Richie\n|Stevie Wonder\n|We are the World\n|We are the World (instrumental)\n\n|artist 1\n|artist 1\n|artist 1\n|track 1\n|track 1\n\n|===\n\n[source,java]\n----\nList\u003cAlbum\u003e albums = Poiji.fromExcel(new File(\"album.xls\"), Album.class);\nalbums.size();\n// 2\nAlbum album1 = albums.get(0);\n// artists = { Artist = [Michael Jackson, Lionel Richie, Stevie Wonder] }\n// tracks = { Track1 = [We are the World], Track2 = [We are the World (instrumental)] }\nAlbum album2 = albums.get(1);\n// artists = {Artist = [artist 1, artist 1, artist 1] }\n// tracks = {Track2 = [track 1], Track1=[track 1] }\n----\n\nJson presentation for `album1` will be as follows\n\n[source,json]\n----\n{\n  \"artists\": {\n    \"Artist\": [\n      \"Michael Jackson\",\n      \"Lionel Richie\",\n      \"Stevie Wonder\"\n    ]\n  },\n  \"tracks\": {\n    \"Track1\": [\n      \"We are the World\"\n    ],\n    \"Track2\": [\n      \"We are the World (instrumental)\"\n    ]\n  }\n}\n----\n\n=== ExcelCellRange Annotation\n\nConsider you have a table like below:\n\n|===\n.2+|No. 5+|Personal Information 3+| Credit Card Information\n|Name | Age | City | State | Zip Code | Card Type | Last 4 Digits | Expiration Date\n\n|1\n|John Doe\n|21\n|Vienna\n|Virginia\n|22349\n|VISA\n|1234\n|Jan-21\n\n|2\n|Jane Doe\n|28\n|Greenbelt\n|Maryland\n|20993\n|MasterCard\n|2345\n|Jun-22\n\n|3\n|Paul Ryan\n|19\n|Alexandria\n|Virginia\n|22312\n|JCB\n|4567\n|Oct-24\n\n|===\n\nThe `ExcelCellRange` annotation lets us aggregate a range of information in one object model. In this case, we collect the data in `PersonCreditInfo` plus details of the person in `PersonInfo` and for the credit card in `CardInfo`:\n\n[source,java]\n----\npublic class PersonCreditInfo {\n\n    @ExcelCellName(\"No.\")\n    private Integer no;\n\n    @ExcelCellRange\n    private PersonInfo personInfo;\n\n    @ExcelCellRange\n    private CardInfo cardInfo;\n\n    public static class PersonInfo {\n        @ExcelCellName(\"Name\")\n        private String name;\n        @ExcelCellName(\"Age\")\n        private Integer age;\n        @ExcelCellName(\"City\")\n        private String city;\n        @ExcelCellName(\"State\")\n        private String state;\n        @ExcelCellName(\"Zip Code\")\n        private String zipCode;\n    }\n\n    public static class CardInfo {\n        @ExcelCellName(\"Card Type\")\n        private String type;\n        @ExcelCellName(\"Last 4 Digits\")\n        private String last4Digits;\n        @ExcelCellName(\"Expiration Date\")\n        private String expirationDate;\n    }\n}\n----\n\nUsing the conventional way, we can retrieve the data using `Poiji.fromExcel`:\n\n[source,java]\n----\nPoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().headerCount(2).build();\nList\u003cPersonCreditInfo\u003e actualPersonalCredits = Poiji.fromExcel(new File(path), PersonCreditInfo.class, options);\n\nPersonCreditInfo personCreditInfo1 = actualPersonalCredits.get(0);\nPersonCreditInfo.PersonInfo expectedPerson1 = personCreditInfo1.getPersonInfo();\nPersonCreditInfo.CardInfo expectedCard1 = personCreditInfo1.getCardInfo();\n----\n\n=== Support Consumer Interface\n\nPoiji supports Consumer Interface. As https://github.com/ozlerhakan/poiji/pull/39#issuecomment-409521808[@fmarazita] explained the usage, there are several benefits of having a Consumer:\n\n1. Huge excel file ( without you have all in memory)\n2. Run time processing/filtering data\n3. DB batch insertion\n\nFor example, we have a Calculation entity class and want to insert each row into a database while retrieving:\n\n[source, java]\n----\nclass Calculation {\n\n  @ExcelCell(0)\n  String name\n\n  @ExcelCell(1)\n  int a\n\n  @ExcelCell(2)\n  int b\n\n  public int getA(){\n    return a;\n  }\n\n  public int getB(){\n    return b;\n  }\n\n  public int getName(){\n    return name;\n  }\n\n}\n----\n\n[source, java]\n----\nFile fileCalculation = new File(example.xlsx);\n\nPoijiOptions options = PoijiOptionsBuilder.settings().sheetIndex(1).build();\n\nPoiji.fromExcel(fileCalculation, Calculation.class, options, this::dbInsertion);\n\nprivate void dbInsertion(Calculation siCalculation) {\n  int value= siCalculation.getA() + siCalculation.getB();\n  String name = siCalculation.getName();\n  insertDB(name , value);\n}\n----\n\n=== Custom Casting Implementation\n\nYou can create your own casting implementation without relying on the default Poiji casting configuration using the `Casting` interface.\n\n[source,java]\n----\nimport com.poiji.config.DefaultCasting;\n\npublic class ModifiedBooleanCasting extends DefaultCasting {\n    @Override\n    Boolean booleanValue(String value, String sheetName, int row, int col, PoijiOptions options) {\n        if (!value.equals(\"y\") \u0026\u0026 !value.equals(\"n\")) {\n            return onError(value, sheetName, row, col, bpe, options.preferNullOverDefault() ? null : false);\n        } else {\n            return value.equals(\"y\");\n        }\n    }\n}\n\npublic class Person {\n\n    @ExcelCell(0)\n    protected Boolean employed;\n\n    @ExcelCell(1)\n    protected String name;\n\n    @ExcelCell(2)\n    protected String surname;\n\n}\n----\n\nThen you can add your custom implementation with the `withCasting` method:\n\n[source,java]\n----\n PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()\n                .withCasting(new ModifiedBooleanCasting())\n                .build();\n\nList\u003cPerson\u003e people = Poiji.fromExcel(excel, Person.class, options);\n----\n\n=== Parse UnknownCells\n\nYou can annotate a `Map\u003cString, String\u003e` with `@ExcelUnknownCells` to parse all entries,\nwhich are not mapped in any other way (for example by index or by name).\n\nThis is our object model:\n\n[source,java]\n----\npublic class MusicTrack {\n\n    @ExcelCellName(\"ID\")\n    private String employeeId;\n\n    @ExcelCellName(\"AUTHOR\")\n    private String author;\n\n    @ExcelCellName(\"NAME\")\n    private String name;\n\n    @ExcelUnknownCells\n    private Map\u003cString, String\u003e unknownCells;\n\n}\n----\n\nThis is the excel file we want to parse:\n\n|===\n|ID | AUTHOR |NAME |ENCODING |BITRATE\n\n|123923\n|Joe Doe\n|The example song\n|mp3\n|256\n\n|56437\n|Jane Doe\n|The random song\n|flac\n|1500\n|===\n\nThe object corresponding to the first row of the excel sheet then has a map with `{ENCODING=mp3, BITRATE=256}`\nand the one for the second row has `{ENCODING=flac, BITRATE=1500}`.\n\nNote that If you use the `PoijiOptionsBuilder#caseInsensitive(true)` option, the ExcelUnknownCells map will be parsed with lowercase.\n\n=== Optional Mandatory Headers and Cells\n\nAs of 4.0.0, `@ExcelCellName` and `@ExcelCell` are given to provide `mandatoryHeader` and `mandatoryCell` fields, meaning we can be more specific by declaring rules in headers or cells or both in excel files. `mandatoryHeader` expects the labeled excel column exists in a given excel file, while `mandatoryCell` looks into the values of the mentioned header/column. If any of the options is active and your excel breaks the rule, either a `HeaderMissingException` or `PoijiMultiRowException`  will be thrown accordingly.\n\n----\n// default behavior\n@ExcelCellName(value = \"COLUMN_NAME\", mandatoryHeader = false, mandatoryCell = false)\nString fieldName;\n\n// default behavior\n@ExcelCell(value = COLUMN_INDEX, mandatoryHeader = false, mandatoryCell = false)\nString fieldName;\n----\n\n[NOTE]\n====\nThe `mandatoryHeader` field is compatible with XLS and XLSX files.\n====\n\n[NOTE]\n====\nThe `mandatoryCell` field works **only** with XLS files and `Sheet` instances. XLS workbooks are opened with `RETURN_BLANK_AS_NULL` missing cell policy. If passing a `Sheet` instance it is up for the caller to make sure the missing cell policy of the parent workbook is set accordingly.\n====\n\n=== Debug Cells Formats\n\nWe can observe each cell format of a given excel file. Assume that we have an excel file like below:\n\n|===\n|Date\n|12/31/2020 12.00 AM\n|===\n\nWe can get all the list of cell formats using `PoijiLogCellFormat` with `PoijiOptions`:\n\n----\nPoijiLogCellFormat log = new PoijiLogCellFormat();\nPoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()\n        .poijiCellFormat(log)\n        .build();\nList\u003cModel\u003e dates = Poiji.fromExcel(stream, poijiExcelType, Model.class, options);\n\nModel model = rows.get(0)\nmodel.getDate();\n// 12.00\n----\n\nHmm, It looks like we did not achieve the correct date format since we get the date value as (`12.00`). Let's see how internally the excel file is being parsed via `PoijiLogCellFormat`:\n\n----\nList\u003cInternalCellFormat\u003e formats = log.formats();\nInternalCellFormat cell10 = formats.get(1);\n\ncell10.getFormatString()\n// mm:ss.0\ncell10.getFormatIndex()\n// 47\n----\n\nNow that we know the reason of why we don't see the expected date value, it's because the default format of the date cell is the `mm:ss.0` format with a given index 47, we need to change the default format of index (i.e. `47`). This format was automatically assigned to the cell having a number, but almost certainly with a special style or format. Note that this option should be used for debugging purpose only.\n\n=== Modify Cells Formats\n\nWe can change the default format of a cell using `PoijiNumberFormat`. Recall `Debug Cells Formats`, we are unable to see the correct cell format, what's more the excel file uses another format which we do not want to.\n\n|===\n|Date\n|12/31/2020 12.00 AM\n|===\n\nUsing `PoijiNumberFormat` option, we are able to change the behavior of the format of a specific index:\n\n----\nPoijiNumberFormat numberFormat = new PoijiNumberFormat();\nnumberFormat.putNumberFormat((short) 47, \"mm/dd/yyyy hh.mm aa\");\n\nPoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()\n        .poijiNumberFormat(numberFormat)\n        .build();\n\nList\u003cModel\u003e rows = Poiji.fromExcel(stream, poijiExcelType, Model.class, options);\n\nModel model = rows.get(0)\nmodel.getDate();\n// 12/31/2020 12.00 AM  \u003c1\u003e\n----\n1. Voila!\n\nWe know that the index 47 uses the format `mm:ss.0` by default in the given excel file, thus we're able to override its format with `mm/dd/yyyy hh.mm aa` using the `putNumberFormat` method.\n\n=== Read Excel Properties\n\nIt is possible to read excel properties from xlsx files. To achieve that, create a class with fields annotated with `@ExcelProperty`.\n\nExample:\n\n[source,java]\n----\npublic class ExcelProperties {\n    @ExcelProperty\n    private String title;\n\n    @ExcelProperty\n    private String customProperty;\n}\n----\n\nThe field name corresponds to the name of the property inside the Excel file.\nTo use a different one than the field name, you can specify a `propertyName` (e.g. `@ExcelProperty(propertyName = \"customPropertyName\")`)\n\nThe list of built-in (e.g. non-custom) properties in an Excel file, which can be read by Poiji can be found in the class `DefaultExcelProperties`.\n\nPoiji can only read Text properties from an Excel file, so you have to use a `String` to read them.\nThis does not apply to \"modified\", \"lastPrinted\" and \"created\", which are deserialized into a `Date`.\n\n\n=== Disable Cells Formats\n\nConsider we have a xls or xlsx excel file like below:\n\n|===\n|Amount\n|25,00\n|(50,00)\n|(65,00)\n|===\n\nSince we use a cell format on line 4 and 5 (i.e. `(50,00)` and `(65,00)`), we don't want to see the formatted value of each cell after processing. In order to do that, we can use `@DisableCellFormatXLS` on a field if the file ends with `xls` or `disableXLSXNumberCellFormat()` for xlsx files using `PoijiOptions`.\n\n.xls files\n----\npublic class TestInfo {\n    @ExcelCell(0)\n    @DisableCellFormatXLS \u003c1\u003e\n    public BigDecimal amount;\n}\n----\n1. we only disable cell formats on the specified column.\n\n.xlsx files\n----\npublic class TestInfo {\n    @ExcelCell(0)\n    private BigDecimal amount;\n}\n\nPoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()\n                .disableXLSXNumberCellFormat() \u003c1\u003e\n                .build();\n----\n1. when disabling *number* cell format, we disable it in the entire cells for xlsx files.\n\nand let Poiji ignores the cell formats:\n\n----\nList\u003cTestInfo\u003e result = Poiji.fromExcel(new File(path), TestInfo.class, options); \u003c1\u003e\n\nresult.get(1).amount\n// -50\n----\n1. Add `options`, if your excel is xlsx file.\n\n=== Create Custom Formatting\n\nYou can create your own formatting implementation without relying on the default Poiji formatting configuration using the `Formatting` interface.\n\n[source,java]\n----\npublic class MyFormatting implements Formatting {\n    @Override\n    public String transform(PoijiOptions options, String value) {\n        return value.toUpperCase().trim(); \u003c1\u003e\n    }\n}\n\npublic class Person {\n\n    @ExcelCellName(\"ID\")\n    protected String employeeId;\n\n    @ExcelCellName(\"NAME\")\n    protected String name;\n\n    @ExcelCellName(\"SURNAME\")\n    protected String surname;\n\n}\n----\n\u003c1\u003e Suppose that all the header names of an excel file have different formatting. Using custom formatting, we are able to look at headers with a custom format. All the headers will be uppercase and don't have white spaces before and after.\n\nThen you can add your custom implementation with the `withFormatting` method:\n\n[source,java]\n----\nPoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()\n                .withFormatting(new MyFormatting())\n                .build();\nList\u003cPerson\u003e people = Poiji.fromExcel(excel, Person.class, options);\n----\n\n=== Poi Sheet Support\n\nPoiji accepts excel records via Poi Sheet object as well:\n\n[source,java]\n----\nFile file = new File(\"/tmp/file.xlsx\");\nFileInputStream fileInputStream = new FileInputStream(file);\nWorkbook workbook = new XSSFWorkbook(fileInputStream);\nSheet sheet = workbook.getSheetAt(0);\n\nList\u003cModel\u003e result = Poiji.fromExcel(sheet, Model.class);\n----\n\n=== Update Default Locale\n\nFor parsing numbers and dates java.lang.Locale is used. Also Apache Poi uses the Locale for parsing.\nAs default, Poij uses Locale.US irrespective of Locale used on the running system. If you want to change that\nyou can use a option to pass the Locale to be used like shown below.\n\nIn this example the Jvm default locale is used. Beware that if your code run's on a other Jvm with another Locale set as default parsing could give different results. Better is to use a fixed locale.\nAlso be aware of differences how Locales behave between Java 8 and 9+. For example AM/PM in Locale.GERMANY is displayed as AM/PM in Java 8 but Vorn./Nam. in Java 9 or higher.\nThis is due to the changes in Java 9. See https://openjdk.java.net/jeps/252[JEP-252] for more details.\n\n[source,java]\n----\nPoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings()\n                .setLocale(Locale.getDefault())\n                .build();\n----\n\n== License\n\nimage:https://app.fossa.com/api/projects/git%2Bgithub.com%2Fozlerhakan%2Fpoiji.svg?type=large[\"FOSSA Status\", link=\"https://app.fossa.com/projects/git%2Bgithub.com%2Fozlerhakan%2Fpoiji?ref=badge_large\"]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozlerhakan%2Fpoiji","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fozlerhakan%2Fpoiji","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozlerhakan%2Fpoiji/lists"}