{"id":42547910,"url":"https://github.com/touchbit/form-urlencoded-marshaller","last_synced_at":"2026-01-28T18:50:02.698Z","repository":{"id":43217161,"uuid":"463891524","full_name":"touchbit/form-urlencoded-marshaller","owner":"touchbit","description":"The marshaller allows you to convert a FormUrlEncoded string to a POJO/Map object and vice versa. Supports nesting of objects, lists, arrays. Supports indexed and non-indexed lists.","archived":false,"fork":false,"pushed_at":"2022-03-14T15:18:56.000Z","size":1100,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-19T00:12:06.701Z","etag":null,"topics":["formurlencoded","java","marshalling","pojo","urlencode","urlencoded","urlencoder"],"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/touchbit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-26T15:31:04.000Z","updated_at":"2022-10-21T08:56:49.000Z","dependencies_parsed_at":"2022-09-06T23:31:00.440Z","dependency_job_id":null,"html_url":"https://github.com/touchbit/form-urlencoded-marshaller","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/touchbit/form-urlencoded-marshaller","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fform-urlencoded-marshaller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fform-urlencoded-marshaller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fform-urlencoded-marshaller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fform-urlencoded-marshaller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/touchbit","download_url":"https://codeload.github.com/touchbit/form-urlencoded-marshaller/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fform-urlencoded-marshaller/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28849169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T15:15:36.453Z","status":"ssl_error","status_checked_at":"2026-01-28T15:15:13.020Z","response_time":57,"last_error":"SSL_read: 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":["formurlencoded","java","marshalling","pojo","urlencode","urlencoded","urlencoder"],"created_at":"2026-01-28T18:49:59.002Z","updated_at":"2026-01-28T18:50:02.688Z","avatar_url":"https://github.com/touchbit.png","language":"Java","readme":"# Marshaller for \"x-www-form-urlencoded\" data\n\n[![Build](https://github.com/touchbit/form-urlencoded-marshaller/actions/workflows/build-deploy.yml/badge.svg?style=plastic)](https://github.com/touchbit/form-urlencoded-marshaller/actions/workflows/build-deploy.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=touchbit_form-urlencoded-marshaller\u0026metric=alert_status\u0026style=plastic)](https://sonarcloud.io/summary/overall?id=touchbit_form-urlencoded-marshaller)\n\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=touchbit_form-urlencoded-marshaller\u0026metric=security_rating\u0026style=plastic)](https://sonarcloud.io/summary/overall?id=touchbit_form-urlencoded-marshaller) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=touchbit_form-urlencoded-marshaller\u0026metric=reliability_rating\u0026style=plastic)](https://sonarcloud.io/summary/overall?id=touchbit_form-urlencoded-marshaller) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=touchbit_form-urlencoded-marshaller\u0026metric=sqale_rating\u0026style=plastic)](https://sonarcloud.io/summary/overall?id=touchbit_form-urlencoded-marshaller) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=touchbit_form-urlencoded-marshaller\u0026metric=coverage\u0026style=plastic)](https://sonarcloud.io/summary/overall?id=touchbit_form-urlencoded-marshaller) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=touchbit_form-urlencoded-marshaller\u0026metric=ncloc\u0026style=plastic)](https://sonarcloud.io/summary/overall?id=touchbit_form-urlencoded-marshaller)\n\n---\n\n![](https://img.shields.io/badge/Java-8%2B-blue?style=plastic\u0026logo=java) [![](https://maven-badges.herokuapp.com/maven-central/org.touchbit.web/form-urlencoded-marshaller/badge.svg?style=plastic)](https://mvnrepository.com/artifact/org.touchbit.web)\n\nThe marshaller allows you to convert a `form-urlencoded` string to a POJO/Map object and vice versa. Supports nesting of objects, lists, arrays. Supports indexed and non-indexed lists.\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.touchbit.web\u003c/groupId\u003e\n    \u003cartifactId\u003eform-urlencoded-marshaller\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n```text\norg.touchbit.web:form-urlencoded-marshaller:jar:1.0.0\n+- org.apache.commons:commons-lang3:jar:3.12.0:compile\n```\n\n---\n\n## TOC\n\n- [Features](#features)\n- [General information](#general-information)\n  - [Settings](#settings)\n- [Usage](#usage)\n  - [Simple POJO (flat data)](#simple-pojo-flat-data)\n  - [Complex POJO (nested objects)](#complex-pojo-nested-objects)\n  - [Additional properties](#additional-properties)\n- [Error handling](#error-handling)\n- [Benchmarks](#benchmarks)\n  - [Brief results](#brief-results)\n  - [Detailed results](#detailed-results)\n- [Licensing](#licensing)\n\n---\n\n## Features\n\n* Marshal POJO/`Map\u003cString, Object\u003e` to URL form data string.\n* Unmarshal URL form data string to POJO/`Map\u003cString, Object\u003e`.\n* Support for nested POJO/Map `foo[bar]=100` \u003c-\u003e `{foo={bar=100}}`.\n* Support for hidden arrays `foo=100\u0026foo=200...\u0026foo=100500`.\n* Support for implicit arrays `foo[]=100\u0026foo[]=200...\u0026foo[]=100500`.\n* Support for explicit arrays `foo[0]=100\u0026foo[1]=200...\u0026foo[n]=100500`.\n* Converting string values to POJO field types.\n* Rules for handling null values (ignore, null string, empty string, null marker).\n* AdditionalProperties field for extra form data parameters (like Jackson2).\n\n[Back to top](#toc)\n\n## General information\n\nFormUrlMarshaller is not a utility class. You can inherit from it and change the current implementation of internal methods to suit your needs. All internal methods have the `protected` modifier. There are no finalized methods or classes.\n\nMarshaling can be done in three ways:\n- `String marshal(Object)` - converts a POJO or Map to a string in `form URL encoded` format.\n- `Map\u003cString, List\u003cString\u003e\u003e marshalToMap(Object)` - converts a POJO or Map to a `form URL encoded` Map where key - URL form Key, value - list of encoded values. For example: `{foo=[1, 2], bar=car} \u003c--\u003e {foo=[1, 2], bar=[car]}`. Allows you to implement your own processing of `form URL encoded` lists.\n- `IChain marshalToIChain(Object)`  - converts a POJO or Map to `IChain` object. `IChain` - this is a chain of encoded url form parameters. Allows you to implement your own processing of `form URL encoded` string data.\n\nUnmarshaling can be done in two ways:\n- `\u003cM\u003e void unmarshalTo(M, String)` - write `form URL encoded` data to a POJO or Map object. \n- `\u003cM\u003e M unmarshal(Class\u003cM\u003e, String)` - write `form URL encoded` data to a POJO or Map (independently creates class instances).\n\n[Back to top](#toc)\n\n### Settings\n\n**(D)** - default\n\n```text\nFormUrlMarshaller.INSTANCE\n  .enableHiddenList() - foo=100\u0026foo=200 (D)\n  .enableImplicitList() - foo[]=100\u0026foo[]=200\n  .enableExplicitList() - foo[0]=100\u0026foo[1]=200\n  .setNullValueRule(RULE_IGNORE) - Ignore null value parameters (D)\n  .setNullValueRule(RULE_NULL_MARKER) - /api/call?foo=%00\n  .setNullValueRule(RULE_EMPTY_STRING) - /api/call?foo=\n  .setNullValueRule(RULE_NULL_STRING) - /api/call?foo=null\n  .prohibitAdditionalProperties(true) - error if extra fields received (D false)\n  .setFormUrlCodingCharset(UTF_16); - value encoding (D utf-8)\n```\n\n[Back to top](#toc)\n\n## Usage\n\n### Simple POJO (flat data)\n\n```java\n@lombok.Data\n@FormUrlEncoded\npublic class Pagination {\n\n  @FormUrlEncodedField(\"limit\")\n  private Integer limit;\n\n  @FormUrlEncodedField(\"offset\")\n  private Integer offset;\n\n}\n```\n\n**Usage**\n\n```java\npublic class Example {\n\n  public static void main(String[] args) {\n    final FormUrlMarshaller marshaller = FormUrlMarshaller.INSTANCE;\n    final Pagination pagination = new Pagination().limit(50).offset(10);\n\n    final String form = marshaller.marshal(pagination);\n    System.out.println(\"UrlEncoded form: \" + form);\n\n    final Pagination pojo = marshaller.unmarshal(Pagination.class, form);\n    System.out.println(\"Pagination POJO: \" + pojo);\n  }\n}\n```\n\n**Output**\n\n```text\nUrlEncoded form: offset=10\u0026limit=50\nPagination POJO: {offset=10, limit=50}\n```\n\n[Back to top](#toc)\n\n### Complex POJO (nested objects)\n\n```java\n@lombok.Data\n@FormUrlEncoded\npublic static class QueryParam {\n\n  @FormUrlEncodedField(\"sorting\")\n  private String sorting;\n\n  @FormUrlEncodedField(\"exclude\")\n  private String exclude;\n\n  // POJO from the previous example\n  @FormUrlEncodedField(\"paginate\")\n  private Pagination paginate;\n\n}\n```\n\n**Usage**\n\n```java\npublic class Example {\n\n  public static void main(String[] args) {\n    FormUrlMarshaller marshaller = FormUrlMarshaller.INSTANCE;\n    Pagination paginate = new Pagination().limit(50).offset(10);\n    QueryParam query = new QueryParam()\n            .exclude(\"\u003c,\u003e\").sorting(\"DESC\").paginate(paginate);\n   \n    final String form = marshaller.marshal(query);\n    System.out.println(\"UrlEncoded form: \" + form);\n   \n    final QueryParam pojo = marshaller.unmarshal(QueryParam.class, form);\n    System.out.println(\"QueryParam POJO: \" + pojo);\n  }\n}\n```\n\n**Output**\n\n```text\nUrlEncoded form: paginate[offset]=10\u0026paginate[limit]=50\u0026sorting=DESC\u0026exclude=%3C%2C%3E\nQueryParam POJO: {exclude=\u003c,\u003e, sorting=DESC, paginate={offset=10, limit=50}}\n```\n\n[Back to top](#toc)\n\n## Additional properties\n\nUsed during unmarshalling to store extra fields that are not present in the POJO model.   \nAP field must have the `@FormUrlEncodedAdditionalProperties` annotation.   \nAP field type - strictly `Map\u003cString, Object\u003e`.  \n\n```java\n@lombok.Data\n@FormUrlEncoded\npublic static class FormData {\n\n  @FormUrlEncodedField(\"firstName\")\n  private String firstName;\n\n  @FormUrlEncodedField(\"lastName\")\n  private String lastName;\n\n  @FormUrlEncodedAdditionalProperties()\n  public Map\u003cString, Object\u003e additionalProperties;\n\n}\n```\n\n**Usage**\n\n```java\npublic class Example {\n\n  public static void main(String[] args) {\n    FormUrlMarshaller marshaller = FormUrlMarshaller.INSTANCE;\n    final String data =\n            \"lastName=Pearson\u0026firstName=Michael\u0026nickname=Gentlemen\";\n    final FormData unmarshal = marshaller.unmarshal(FormData.class, data);\n    System.out.println(\"QueryParam POJO: \" + unmarshal);\n  }\n}\n```\n\n**Output**\n\n```text\nUrlEncoded form: lastName=Pearson\u0026firstName=Michael\u0026nickname=Gentlemen\nQueryParam POJO: {firstName=Michael, lastName=Pearson, additionalProperties={nickname=Gentlemen}}\n```\n\n**Prohibition of extra fields**\n\n```java\npublic class Example {\n\n  public static void main(String[] args) {\n    FormUrlMarshaller marshaller = FormUrlMarshaller.INSTANCE\n            .prohibitAdditionalProperties(true); // \u003c-------- PROHIBIT\n    final String data = \"lastName=Pearson\u0026\" +\n                        \"firstName=Michael\u0026\" +\n                        \"nickname=Gentlemen\";\n    final FormData form = marshaller.unmarshal(FormData.class, data);\n    System.out.println(\"QueryParam POJO: \" + form);\n  }\n}\n```\n\n```text\nMarshallerException: \n  URL encoded string contains unmapped additional properties.\n    Actual: {nickname=Gentlemen}\n    Expected: There are no additional properties.\n```\n\n[Back to top](#toc)\n\n## Error handling\n\nMarshalling and unmarshaling methods only throw `MarshallerException` (`RuntimeException`).   \nErrors during the operation of the marshaller are as detailed as possible. For example, if the FormUrlEncoded string contains an array of objects instead of a single object.\n\n```text\norg.touchbit.www.form.urlencoded.marshaller.util.MarshallerException: \n  Incompatible types received for conversion.\n    Source: {pagination=[{limit=50, offset=10}]}\n    Source field: pagination\n    Source value: [{limit=50, offset=10}]\n    Source type: java.util.ArrayList\n    Target type: qa.model.QueryParams\n    Target field: private Pagination pagination;\n```\n\n[Back to top](#toc)\n\n## Benchmarks\n\n```text\nJMH version: 1.34\nVM version: JDK 15.0.4, Zulu OpenJDK 64-Bit Server VM, 15.0.4+5-MTS\nBlackhole mode: full + dont-inline hint\nWarmup: 5 iterations, 10 s each\nMeasurement: 5 iterations, 10 s each\nThreads: 4 threads, will synchronize iterations\nBenchmark mode: Average time, time/op\n```\n\n- Each benchmark contains a pre-prepared set of data for marshaling and unmarshaling.\n- Marshalling and unmarshaling is checked on data of `[16,32,64...1024]` bytes length.\n- As the data size increases, the **number of involved object fields increases**. An example for clarity:\n    - 16 byte -\u003e `offset=0\u0026limit=5`\n    - 32 byte -\u003e `offset=2\u0026limit=50\u0026sort=ASC\u0026foo=1`\n- GC is performed between measurements.\n- The results of marshaling and unbarshaling are dumped into the `Blackhole`.\n- Used 6 load profiles:\n    - FormUrlEncoded String \u003c--\u003e `Map\u003cString, String\u003e`\n    - FormUrlEncoded String \u003c--\u003e POJO with fields of type `String`\n    - FormUrlEncoded String \u003c--\u003e POJO with fields of type `Integer`\n    - FormUrlEncoded String \u003c--\u003e POJO with fields of type `LIst\u003cString\u003e`\n    - FormUrlEncoded String \u003c--\u003e POJO with fields of type `LIst\u003cString\u003e`\n    - FormUrlEncoded String \u003c--\u003e POJO with nested POJOs fields\n\n### Brief results\n\n![](./.benchmarks/img/MarshalBrief.png?raw=true)\n![](./.benchmarks/img/UnmarshalBrief.png?raw=true)\n\n[Back to top](#toc)\n\n### Detailed results\n\nClick image for detailed JMH report\n\n[![](./.benchmarks/img/MapStringString.png?raw=true)](https://jmh.morethan.io/?source=https://raw.githubusercontent.com/touchbit/form-urlencoded-marshaller/main/.benchmarks//POJO_field_type_String-report.json?raw=true)\n[![](./.benchmarks/img/PojoString.png?raw=true)](https://jmh.morethan.io/?source=https://raw.githubusercontent.com/touchbit/form-urlencoded-marshaller/main/.benchmarks//POJO_field_type_String-report.json?raw=true)\n[![](./.benchmarks/img/PojoInteger.png?raw=true)](https://jmh.morethan.io/?source=https://raw.githubusercontent.com/touchbit/form-urlencoded-marshaller/main/.benchmarks//POJO_field_type_Integer-report.json?raw=true)\n[![](./.benchmarks/img/PojoListString.png?raw=true)](https://jmh.morethan.io/?source=https://raw.githubusercontent.com/touchbit/form-urlencoded-marshaller/main/.benchmarks//POJO_field_type_List_String-report.json?raw=true)\n[![](./.benchmarks/img/PojoListInteger.png?raw=true)](https://jmh.morethan.io/?source=https://raw.githubusercontent.com/touchbit/form-urlencoded-marshaller/main/.benchmarks//POJO_field_type_List_Integer-report.json?raw=true)\n[![](./.benchmarks/img/PojoNestedPojo.png?raw=true)](https://jmh.morethan.io/?source=https://raw.githubusercontent.com/touchbit/form-urlencoded-marshaller/main/.benchmarks//POJO_field_type_nested_POJO-report.json?raw=true)\n\n[Back to top](#toc)\n\n## Licensing\n\nCopyright (c) 2022 Shaburov Oleg.   \nDistributed under license 'Apache License Version 2.0'.   \nSee the [LICENSE](./LICENSE?raw=true) file for license rights and limitations.   \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftouchbit%2Fform-urlencoded-marshaller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftouchbit%2Fform-urlencoded-marshaller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftouchbit%2Fform-urlencoded-marshaller/lists"}