{"id":15044215,"url":"https://github.com/buckelieg/validation-fn","last_synced_at":"2026-01-02T00:08:41.375Z","repository":{"id":65507706,"uuid":"520180106","full_name":"buckelieg/validation-fn","owner":"buckelieg","description":"Microlibrary for validation","archived":false,"fork":false,"pushed_at":"2024-11-28T15:26:48.000Z","size":156,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-20T13:23:29.439Z","etag":null,"topics":["functional","functional-programming","java","java-8","validation","validator"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/buckelieg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2022-08-01T16:15:52.000Z","updated_at":"2024-11-28T15:26:52.000Z","dependencies_parsed_at":"2024-11-19T10:49:55.318Z","dependency_job_id":"4f63b7a0-03f5-4101-b13b-d187601b7c93","html_url":"https://github.com/buckelieg/validation-fn","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buckelieg%2Fvalidation-fn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buckelieg%2Fvalidation-fn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buckelieg%2Fvalidation-fn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buckelieg%2Fvalidation-fn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/buckelieg","download_url":"https://codeload.github.com/buckelieg/validation-fn/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243451605,"owners_count":20293168,"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":["functional","functional-programming","java","java-8","validation","validator"],"created_at":"2024-09-24T20:50:17.850Z","updated_at":"2026-01-02T00:08:41.335Z","avatar_url":"https://github.com/buckelieg.png","language":"Java","readme":"[![build](https://github.com/buckelieg/validation-fn/workflows/build/badge.svg?branch=master)]()\n[![license](https://img.shields.io/github/license/buckelieg/validation-fn.svg)](./LICENSE.md)\n[![dist](https://img.shields.io/maven-central/v/com.github.buckelieg/validation-fn.svg)](http://mvnrepository.com/artifact/com.github.buckelieg/validation-fn)\n[![javadoc](https://javadoc.io/badge2/com.github.buckelieg/validation-fn/javadoc.svg)](https://javadoc.io/doc/com.github.buckelieg/validation-fn)\n# validation-fn\nFunctional style validation for Java\n\n## Quick reference\n\nAdd maven dependency:\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.buckelieg\u003c/groupId\u003e\n  \u003cartifactId\u003evalidation-fn\u003c/artifactId\u003e\n  \u003cversion\u003e0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\nThere no transitive dependencies\n\n### Simple validators\n\n```java\nValidator\u003cInteger\u003e validator = Validators.\u003cInteger\u003enotNull(\"Value must not be null\")\n                                        .then(Predicates.\u003cInteger\u003enotIn(20, 789, 1001), v -\u003e String.format(\"Value of '%s' must be one of:  [20, 789, 1001]\", v));\n// then constructed validator is used to validate an arbitrary values:\nInteger value = validator.validate(null); // throws: \"Value must not be null\"\nint value = validator.validate(8); // throws: \"Value of '8' must be one of:  [20, 789, 1001]\"\n```\n\n### Complex validators\nConsider we have the next domain model:\n```java\npublic class Address {\n\n    private String city;\n    private String street;\n    private long buildingNumber;\n\n    public Address(String city, String street, long buildingNumber) {\n        this.city = city;\n        this.street = street;\n        this.buildingNumber = buildingNumber;\n    }\n\n    public Address() {\n    }\n\n    // getters and setters are dropped for sanity\n}\npublic class Person {\n\n    private String firstName;\n    private String secondName;\n    private String lastName;\n    private int age;\n    private List\u003cAddress\u003e addresses;\n    private Optional\u003cString\u003e gender;\n\n    public Person(String firstName, String secondName, String lastName, int age, Address... addresses) {\n        this.firstName = firstName;\n        this.secondName = secondName;\n        this.lastName = lastName;\n        this.age = age;\n        this.addresses = null == addresses ? null : Arrays.asList(addresses);\n    }\n\n    public Person() {\n    }\n    \n    // getters and setter are dropped for sanity\n}\n```\nUser might have a couple of addresses (or might have none) and gender is optional to specify.\nThis structure somehow brought to our service (e.g. in Data Transfer Object or smth) and it is needed to validated.\nHere we have some optional validation paths which must be executed only if corresponding data exists.\nTherefore, if validated person has no address - we skip those validation checks for address, but\nif some address is present - we must check it for correctness.\nLet's take a look to a code which will show us how it would be done using this library:\n```java\n// Our potential addresses\nAddress address1 = new Address(\"MyCity\", \"MyStreet\", 13);\nAddress address2 = new Address();\n// test persons\nPerson person1 = new Person(\"FirstName\", \"SecondName\", \"LastName\", 76);\nPerson person2 = new Person(\"FirstName\", \"SecondName\", \"LastName\", 76, address1);\nPerson person3 = new Person(\"FirstName\", \"SecondName\", \"LastName\", 76, address1, address2);\nPerson person4 = new Person(\"FirstName\", \"SecondName\", \"LastName\", -76);\nPerson person5 = new Person(\"First\", \"SecondName\", \"LastName\", 76);\n```\nOk, now we ready to write our validator for those test data:\n```java\nValidator\u003cPerson\u003e validator = Validators.\u003cPerson\u003enotNull(\"Person must be provided\")\n                .thenMap( // unconditionally validating person object field of 'firstName'\n                        Person::getFirstName,\n                        Predicates.of(Strings::isBlank).or(Strings.minLength(6)), // validation case in the form of java.util.Predicate \n                        value -\u003e String.format(\"FirstName '%s' must not be null and at least 6 characters long\", value) // error message provider function - the ValidationException message\n                )\n                .thenMap(\n                        Person::getSecondName, // validating person object field of 'secondName'\n                        Validator.\u003cString\u003eof().thenIf(\n                                Predicates.of(Strings::isBlank).negate(), // field validation condition\n                                Validator.ofPredicate( // construct validator from:\n                                        Strings.minLength(6), // validation test case predicate\n                                        \"Minimum second name length is 6\" // error message if predicate returns TRUE\n                                )\n                        )\n                )\n                .thenMap(Person::getLastName, Strings::isBlank, \"Last name must not be empty\") // unconditionally validating 'lastName'\n                .thenMap(\n                        Person::getAge, // validating 'age' field\n                        Predicates.\u003cInteger\u003eof(Numbers::isNegative).or(Numbers.max(100)), // combine predicates with arbitrary conditions to be validated against\n                        \"Age has to be greater than 0 and less than 100\" // an error message if we fail\n                )\n                .thenMap(\n                        Person::getAddresses, // validating address collection\n                        Validators.eachOf(Validators.\u003cAddress\u003enotNull(\"Address must not be null\")\n                            .thenMap(Address::getCity, Strings::isBlank, \"City must not be blank\")\n                            .thenMap(Address::getStreet, Strings::isBlank, \"Street must not be blank\")\n                            .thenMap(Address::getBuildingNumber, Numbers::isNegative, \"Build number must be positive\")\n                        )\n                )\n                /**\n                 * We are free to implement our validators as we desire. For example if we want to validate address at once - we might write the validator like this:\n                 * \n                 *.thenMap(Person::getAddresses, Validators.eachOf(Validators.\u003cAddress\u003enotNull().then(\n                 *  addr -\u003e Strings.isBlank(addr.getCity()) || Strings.isBlank(addr.getStreet()) || Numbers.isNegative(addr.getBuildingNumber()),\n                 *  addr -\u003e String.format(\"Address of '%s' must be fully filled in\", addr) // if Address.toString() method is implemented fine we obtain reasonable error description\n                 *)))\n                 * \n                 */\n                .thenMap(\n                        Person::getGender, // field 'gender' is optional, so we validating it only if the value is present\n                        Validators.ifPresent( // construct predicate that validates on existing value (i.e. optional object is not null and not empty) - if it is - undegroing validation is not performed\n                              Strings::isBlank, // test redicate \n                              \"Gender must not be blank\" // error message\n                        )\n                );\n```\nAt this stage we are ready to validate our data:\n```java\nvalidator.validate(person1); // throws nothing\nvalidator.validate(person2); // throws nothing\nvalidator.validate(person3); // throws ValidationException with message of \"City must not be blank\" since the second address is not filled at all\nvalidator.validate(person4); // throws ValidationException with message of \"Age has to be greater than 0 and less than 100\" since age is -76\nvalidator.validate(person5); // throws ValidationException with message of \"FirstName 'First' must not be null and at least 6 characters long\" since it is 5 characters long\n```\n### Helper classes\nThere are some helper classes that makes writing code shorter and easier, these are:\n+ Validators - shortcut methods to use Validator\n+ Predicates - generic purpose predicates\n+ Iterables - predicate collection for iterables\n+ Strings - predicate collection for strings\n+ Dates - predicate collection for dates\n+ Numbers - predicate collection for numbers\n+ Maps - predicate collection related to maps\n\nThese are subject to extension. \n\n### Prerequisites\nJava8, Maven.\n\n## License\nThis project licensed under Apache License, Version 2.0 - see the [LICENSE](LICENSE) file for details\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuckelieg%2Fvalidation-fn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbuckelieg%2Fvalidation-fn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuckelieg%2Fvalidation-fn/lists"}