{"id":36601642,"url":"https://github.com/btheu/estivate","last_synced_at":"2026-01-12T08:38:55.882Z","repository":{"id":45559266,"uuid":"54745372","full_name":"btheu/estivate","owner":"btheu","description":"Mapping from DOM to POJO with CSS Query Syntax and annotations.","archived":false,"fork":false,"pushed_at":"2024-03-13T12:38:41.000Z","size":473,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-03T21:53:09.952Z","etag":null,"topics":["annotations","crawler","csquery","css-selector","html","java","jsoup","jsoup-operation","pojo"],"latest_commit_sha":null,"homepage":"","language":"HTML","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/btheu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","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}},"created_at":"2016-03-25T20:19:53.000Z","updated_at":"2022-01-18T13:18:22.000Z","dependencies_parsed_at":"2023-01-17T17:47:26.918Z","dependency_job_id":"394282d4-886b-46d9-ba75-6ae6b52cec72","html_url":"https://github.com/btheu/estivate","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/btheu/estivate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btheu%2Festivate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btheu%2Festivate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btheu%2Festivate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btheu%2Festivate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/btheu","download_url":"https://codeload.github.com/btheu/estivate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/btheu%2Festivate/sbom","scorecard":{"id":256729,"data":{"date":"2025-08-11","repo":{"name":"github.com/btheu/estivate","commit":"db44371a35e4ae7a651cc912a937d5d4d904526a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T09:53:01.135Z","repository_id":45559266,"created_at":"2025-08-17T09:53:01.136Z","updated_at":"2025-08-17T09:53:01.136Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28337599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":["annotations","crawler","csquery","css-selector","html","java","jsoup","jsoup-operation","pojo"],"created_at":"2026-01-12T08:38:55.813Z","updated_at":"2026-01-12T08:38:55.875Z","avatar_url":"https://github.com/btheu.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Estivate\n\nMapping from DOM to POJO with CSS Query Syntax and annotations.\n\nEstivate use \u003ca href=\"http://jsoup.org/\"\u003eJSoup API\u003c/a\u003e for inside CSS queries.\n\n# Getting Started\n\nGiving this simple HTML, we want to the POJO's ```name``` field set with the body of ```#nameId``` element. \n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"nameId\"\u003eThis is my name\u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n```java\npublic class Result {\n  @Text(select=\"#nameId\")\n  public String name;\n}\n```\n\nMapping a DOM document to a POJO is very easy.\n\n```java\nInputStream document = ...\nEstivateMapper mapper = new EstivateMapper();\nResult result = mapper.map(document, Result.class);\n```\n\nDefinition of Result class POJO definition which is:\n\n1. Select an JSoup Element with cssQuery ``` \"#nameId\" ```  on the document.\n* Apply JSoup ``` element.text() ``` on the Element selected.\n* Set the result to the ``` name ``` field.\n\n\n## Download\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.btheu.estivate\u003c/groupId\u003e\n  \u003cartifactId\u003eestivate\u003c/artifactId\u003e\n  \u003cversion\u003e0.4.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Mapping of collection\n\n```java\nInputStream document = ...\nEstivateMapper mapper = new EstivateMapper();\nList\u003cResult\u003e result = mapper.mapToList(document, Result.class);\n```\n\n```java\n@Select(\"div.someClass\")\npublic class Result {\n  @Text(select=\".name\")\n  public String name;\n}\n```\n\n### Annotation on methods\n\nEstivate's annotations can be used directly on methods. \nThis provides a way to implement custom operations just after mapping.\n\n```java\npublic class Result {\n  public String name;\n  \n  @Text(select=\"#nameId\")\n  public void setName(String pName){\n    this.name = pName.substring(0,3).toUpperCase();\n  }\n}\n```\n\n### ```@Select``` \n\nMakes JSoup's ``` element.select(...) ``` operation on the DOM Document.\n\nDo cssQuery on the DOM Document then return the DOM Element corresponding. \nWhen combined with ``` @Text ``` (or ``` @Attr ```), the \nfinal result will be the application of ```text()``` (or ``` attr(...) ```)\non this DOM Element.\n\n```java\npublic class Result {\n  @Text(select=\"div#content \u003e span p\")\n  public String description;\n}\n```\n\nAlso, the JSoup Element object can be mapped to the field or method.\n\n```java\npublic class Result {\n  @Select(select=\"div#content \u003e span p\")\n  public Element paragraphElement;\n}\n```\n\nMethod mapping is a way to perform further JSoup operations.\n\n```java\npublic class Result {\n  public String name;\n  \n  @Select(select=\"div#content \u003e span p\")\n  public void setName(Element pElement){\n    name = pElement.siblingNodes().first().text();\n  }\n}\n```\n\n### ```@Text```\n\nMakes JSoup's ``` element.text() ``` operation on the DOM Element when own attribute is set to false.\n\nMaps the combined text of this element and all its children. Whitespace is \nnormalized and trimmed.\n\n```java\npublic class Result {\n  @Text(select=\"#description\")\n  public String description;\n}\n```\n\nMakes JSoup's ``` element.ownText() ``` operation on the DOM Element when value is true.\n\nMaps the text owned by this element only; does not get the combined text of all children. \n\n```java\npublic class Result {\n  @Text(select=\"#description\")\n  public String description;\n}\n```\n\n### ```@Attr```\n\nMakes JSoup's ``` element.attr(...) ``` operation on the DOM Element.\n\nMaps an attribute's value by its key. To get an absolute URL from an attribute\nthat may be a relative URL, prefix the key with abs, which is a shortcut to\nthe absUrl method. E.g.:\n\n```java\npublic class Result {\n  @Attr(select=\"#picture\", value=\"abs:href\")\n  public String absoluteUrl;\n}\n```\n\n### ```@Table \u0026 @Column```\n\nParse Table HTML DOM and match data by column name\n\nEach column are mapped to java class field/method.\n\n```java\nInputStream document = ...\nEstivateMapper mapper = new EstivateMapper();\nList\u003cResult\u003e result = mapper.mapToList(document, Result.class);\n```\n\n```java\n@Table(select=\"#table1\")\npublic class Result {\n  @Column(\"Number Column\")\n  public int number;\n  @Column(\"Street Column\")\n  public int street;\n  @Column(name=\"Name.*Column\", regex=true)\n  @Attr(select=\"span\", value=\"title\")\n  public String name;\n}\n```\n\n```html\n\u003chtml\u003e\n\u003chead\u003e\n  \u003ctitle\u003etable-u1\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cdiv id=\"content\"\u003e\n\n\n\u003ctable id=\"table1\"\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n  \u003cth\u003e\u003cspan\u003eNumber Column\u003c/span\u003e\u003c/th\u003e\n  \u003cth\u003e\u003cspan\u003eStreet Column\u003c/span\u003e\u003c/th\u003e\n  \u003cth\u003e\u003cspan\u003eName Column\u003c/span\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003cspan\u003e1\u003c/span\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cspan\u003estreetA\u003c/span\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cspan title=\"nameA\"/\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003cspan\u003e2\u003c/span\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cspan\u003estreetB\u003c/span\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cspan title=\"nameB\"/\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\u003cspan\u003e3\u003c/span\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cspan\u003estreetC\u003c/span\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cspan title=\"nameC\"/\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### ```@Is```\n\nMakes JSoup's ``` element.is(...) ``` operation on the DOM Element.\n\nCheck if this element matches the given Selector CSS query.\n\n```java\npublic class Result {\n  @Is(select=\"#setting\", value=\".specific\")\n  public boolean isSpecific;\n}\n```\n\n### ```Optional```\n\nIndicate that Estivate wont throw a exception if the mapping of this field\nor method is not satisfied.\n\n```java\npublic class Result {\n  @Text(select=\"#description\", optional=true)\n  public String description;\n}\n```\n\n### ```@TagName```\n\nMakes JSoup's ``` element.TagName() ``` operation on the DOM Element.\n\nMaps the name of the tag for this element. E.g. div\n\n```java\npublic class Result {\n  @TagName(select=\".picture\", first=true)\n  public String pictureTagName;\n}\n```\n\n### ```@Title```\n\nMakes JSoup's ``` element.title() ``` operation on the DOM Document.\n\nMaps the string contents of the document's title element.\n\n```java\npublic class Result {\n  @Title\n  public String pageTitle;\n}\n```\n\n### ```@Val```\n\nMakes JSoup's ``` element.val() ``` operation on the DOM Element.\n\nMaps the value of a form element (input, textarea, etc).\n\n```java\npublic class Result {\n  @Val(\"#form_field_1\")\n  public String name;\n}\n```\n\n### Recursive mapping\n\n#### Single Element\n\nPOJO can have complexe mapping having sub POJO themself mapped with a sub DOM Element.\n\n```java\npublic class Page {\n  @Select(select=\"div#content2\")\n  public Content content;\n}\n\n/**\n* All fields will be mapped with the sub DOM \n* selected by \u003ccode\u003ePage\u003c/code\u003e content rule\n*/\npublic class Content {\n  @Text(select=\".name\")\n  public String name;\n  \n  @Text(select=\".description\")\n  public String description;\n}\n```\n\nThe ```name``` field will be setted as ```\"Actual name2\"``` with the following HTML.\n\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"content1\"\u003e\n      \u003cdiv class=\"name\"\u003e\n        Actual name1\n      \u003c/div\u003e\n      ...\n      \u003cdiv class=\"description\"\u003e\n        This is the description of content 1.\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv id=\"content2\"\u003e\n      \u003cdiv class=\"name\"\u003e\n        Actual name2\n      \u003c/div\u003e\n      ...\n      \u003cdiv class=\"description\"\u003e\n        This is the description of content 2.\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n#### List of Element\n\n```java\npublic class Page {\n  @Select(select=\"div.article p\")\n  public List\u003cArticle\u003e articles;\n}\n\n/**\n* All fields will be mapped with the sub DOM \n* selected by \u003ccode\u003ePage\u003c/code\u003e articles rule for one \u003ccode\u003eP\u003c/code\u003e\n*/\n// JSoupSelectList is not necessary as long Page already specify the select rule.\npublic class Article {\n  @Text(select=\".author\")\n  public String author;\n  \n  @Text(select=\".date\")\n  public String date;\n}\n```\n\nThis will perfectly macht all aticles giving this HTML DOM.\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv class=\"article\"\u003e\n      \u003cp\u003e\n        \u003cdiv class=\"author\"\u003e\n          Author first article.\n        \u003c/div\u003e\n        ...\n        \u003cdiv class=\"date\"\u003e\n          Nov. 1st 2015\n        \u003c/div\u003e\n      \u003c/p\u003e\n    \u003c/div\u003e\n    ... \n    \u003cdiv class=\"article\"\u003e\n      \u003cp\u003e\n        \u003cdiv class=\"author\"\u003e\n          Author last article.\n        \u003c/div\u003e\n        ...\n        \u003cdiv class=\"date\"\u003e\n          Nov. 30th 2015\n        \u003c/div\u003e\n      \u003c/p\u003e\n    \u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n#### Primitive types\n\nEstivate handles primitive types for fields or methods arguments mapping.\n\n```java\npublic class Rapport {\n  @Text(select=\"#nbTeachers\")\n  public Integer numberOfTeachers;\n  \n  @Text(select=\"#nbStudents\")\n  public int numberOfStudents;\n}\n```\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"nbTeachers\"\u003e\n      123\n    \u003c/div\u003e\n    \u003cdiv id=\"nbStudents\"\u003e\n      456\n    \u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n# License MIT\n\nThe MIT License\n\n© 2016-2023, Benoit Theunissen \u003cbenoit.theunissen@gmail.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbtheu%2Festivate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbtheu%2Festivate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbtheu%2Festivate/lists"}