{"id":22235238,"url":"https://github.com/williamthome/euneus","last_synced_at":"2025-06-26T03:35:57.015Z","repository":{"id":203240977,"uuid":"709127346","full_name":"williamthome/euneus","owner":"williamthome","description":"An incredibly flexible and performant JSON parser, generator and formatter in pure Erlang.","archived":false,"fork":false,"pushed_at":"2025-06-25T03:53:25.000Z","size":1905,"stargazers_count":25,"open_issues_count":10,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-25T04:34:12.612Z","etag":null,"topics":["erlang","erlang-library","format-json","json","jsonformatter","jsongenerator","jsonparser","minify","minify-json","prettify","prettify-json","rebar3"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/euneus","language":"Erlang","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/williamthome.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["williamthome"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":["https://www.buymeacoffee.com/williamthome"]}},"created_at":"2023-10-24T04:20:39.000Z","updated_at":"2025-05-25T23:56:36.000Z","dependencies_parsed_at":"2023-11-14T12:30:46.204Z","dependency_job_id":"7c39799f-b653-4bea-b900-70d80d2fad76","html_url":"https://github.com/williamthome/euneus","commit_stats":null,"previous_names":["williamthome/euneus"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/williamthome/euneus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Feuneus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Feuneus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Feuneus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Feuneus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/williamthome","download_url":"https://codeload.github.com/williamthome/euneus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williamthome%2Feuneus/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261823775,"owners_count":23215150,"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":["erlang","erlang-library","format-json","json","jsonformatter","jsongenerator","jsonparser","minify","minify-json","prettify","prettify-json","rebar3"],"created_at":"2024-12-03T02:12:31.591Z","updated_at":"2025-06-26T03:35:56.984Z","avatar_url":"https://github.com/williamthome.png","language":"Erlang","funding_links":["https://github.com/sponsors/williamthome","https://www.buymeacoffee.com/williamthome"],"categories":[],"sub_categories":[],"readme":"# Euneus\n\n[![Github Actions](https://github.com/williamthome/euneus/workflows/CI/badge.svg)](https://github.com/williamthome/euneus/actions)\n[![Coverage](https://raw.githubusercontent.com/cicirello/jacoco-badge-generator/main/tests/100.svg)](https://github.com/williamthome/euneus/actions/workflows/ci.yml)\n[![Erlang Versions](https://img.shields.io/badge/Erlang%2FOTP-24%2B-green?style=flat-square)](http://www.erlang.org)\n[![Latest Release](https://img.shields.io/github/release/williamthome/euneus.svg?style=flat-square)](https://github.com/williamthome/euneus/releases/latest)\n[![Hex Version](https://img.shields.io/hexpm/v/euneus.svg)](https://hex.pm/packages/euneus)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/euneus/)\n[![Total Download](https://img.shields.io/hexpm/dt/euneus.svg)](https://hex.pm/packages/euneus)\n[![License](https://img.shields.io/hexpm/l/euneus.svg)](https://github.com/williamthome/euneus/blob/main/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/williamthome/euneus.svg)](https://github.com/williamthome/euneus/commits/main)\n\nAn incredibly flexible and performant JSON parser, generator and formatter in pure Erlang.\n\nEuneus is built on the top of the new [OTP json module](https://erlang.org/documentation/doc-15.0-rc3/lib/stdlib-6.0/doc/html/json.html).\n\nBoth encoder and decoder fully conform to [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259)\nand [ECMA 404](https://ecma-international.org/publications-and-standards/standards/ecma-404/) standards\nand are tested using [JSONTestSuite](https://github.com/nst/JSONTestSuite).\n\nDetailed examples and further explanation can be found at [hexdocs](https://hexdocs.pm/euneus).\n\n## Requirements\n\nOTP \u003e= 24.\n\n## Why should you use Euneus over the OTP json module?\n\nThe new OTP `json` module is incredible and blazing fast!\n\nUnfortunately, it is only available for OTP \u003e= 27. Euneus is available from OTP \u003e= 24.\n\nAlso, Euneus simplifies a lot of overheads with the new OTP `json` module without\nlosing any option provided by the json module and keeping its performance.\n\nA simple example comparing the OTP `json` module with Euneus decoding object keys:\n\n```erlang\n\u003e json:decode(\u003c\u003c\"{\\\"foo\\\":\\\"bar\\\"}\"\u003e\u003e, [], #{object_push =\u003e fun(K, V, Acc) -\u003e [{binary_to_atom(K), V} | Acc] end}).\n{#{foo =\u003e \u003c\u003c\"bar\"\u003e\u003e},[],\u003c\u003c\u003e\u003e}\n\u003e euneus:decode(\u003c\u003c\"{\\\"foo\\\":\\\"bar\\\"}\"\u003e\u003e, #{object_keys =\u003e atom}).\n#{foo =\u003e \u003c\u003c\"bar\"\u003e\u003e}\n```\n\n### Encode Features\n\nSome reasons to use Euneus for JSON encoding:\n\n- Possibility to skip values\n- Encoding proplists (proplists are not encoded by the OTP json module)\n- Sort object keys\n- Simple custom encoding via codecs:\n\n  ```erlang\n  -type codec() ::\n      timestamp\n      | datetime\n      | ipv4\n      | ipv6\n      | {records, #{Name :: atom() := {Fields :: [atom()], Size :: pos_integer()}}}\n      | codec_fun()\n      | custom_codec().\n  ```\n\n#### Encode Codecs\n\n##### Encode timestamp\n\n```erlang\n\u003e euneus:encode({0, 0, 0}, #{codecs =\u003e [timestamp]}).\n\u003c\u003c\"\\\"1970-01-01T00:00:00.000Z\\\"\"\u003e\u003e\n```\n\n##### Encode datetime\n\n```erlang\n\u003e euneus:encode({{1970, 01, 01}, {00, 00, 00}}, #{codecs =\u003e [datetime]}).\n\u003c\u003c\"\\\"1970-01-01T00:00:00Z\\\"\"\u003e\u003e\n```\n\n##### Encode ipv4\n\n```erlang\n\u003e euneus:encode({0, 0, 0, 0}, #{codecs =\u003e [ipv4]}).\n\u003c\u003c\"\\\"0.0.0.0\\\"\"\u003e\u003e\n```\n\n##### Encode ipv6\n\n```erlang\n\u003e euneus:encode({16#fe80, 0, 0, 0, 16#204, 16#acff, 16#fe17, 16#bf38}, #{codecs =\u003e [ipv6]}).\n\u003c\u003c\"\\\"fe80::204:acff:fe17:bf38\\\"\"\u003e\u003e\n```\n\n##### Encode record\n\n```erlang\n% -record(foo, {foo, bar}).\n\u003e euneus:encode(#foo{foo = 1, bar = 2}, #{\n    codecs =\u003e [\n      {records, #{\n        foo =\u003e {record_info(fields, foo), record_info(size, foo)}\n      }}\n    ]\n  }).\n\u003c\u003c\"{\\\"foo\\\":1,\\\"bar\\\":2}\"\u003e\u003e\n```\n\n### Decode Features\n\nSome reasons to use Euneus for JSON decoding:\n\n- Faster decoding than the OTP `json` module via some options:\n\n  ```erlang\n  #{\n    array_finish =\u003e reversed,\n    object_finish =\u003e reversed_proplist % or proplist\n  }\n  ```\n\n- The overhead of transforming binary keys to, e.g., atoms\n- Simple custom decoding via codecs:\n\n  ```erlang\n  -type codec() ::\n    copy\n    | timestamp\n    | datetime\n    | ipv4\n    | ipv6\n    | pid\n    | port\n    | reference\n    | codec_callback().\n  ```\n\n#### Decode Object keys\n\n##### Keys to binary (default)\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"{\\\"foo\\\":\\\"bar\\\"}\"\u003e\u003e).\n#{\u003c\u003c\"foo\"\u003e\u003e =\u003e \u003c\u003c\"bar\"\u003e\u003e}\n```\n\n##### Keys copy\n\nJust do a binary copy of the key.\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"{\\\"foo\\\":\\\"bar\\\"}\"\u003e\u003e, #{object_keys =\u003e copy}).\n#{\u003c\u003c\"foo\"\u003e\u003e =\u003e \u003c\u003c\"bar\"\u003e\u003e}\n```\n\n##### Keys to atom\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"{\\\"foo\\\":\\\"bar\\\"}\"\u003e\u003e, #{object_keys =\u003e atom}).\n#{foo =\u003e \u003c\u003c\"bar\"\u003e\u003e}\n```\n\n##### Keys to existing atom\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"{\\\"foo\\\":\\\"bar\\\"}\"\u003e\u003e, #{object_keys =\u003e existing_atom}).\n#{foo =\u003e \u003c\u003c\"bar\"\u003e\u003e}\n```\n\n#### Decode Codecs\n\n##### Decode copy\n\nJust do a binary copy of the value.\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"foo\\\"\"\u003e\u003e, #{codecs =\u003e [copy]}).\n\u003c\u003c\"foo\"\u003e\u003e\n```\n\n##### Decode timestamp\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"1970-01-01T00:00:00.000Z\\\"\"\u003e\u003e, #{codecs =\u003e [timestamp]}).\n{0,0,0}\n```\n\n##### Decode datetime\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"1970-01-01T00:00:00Z\\\"\"\u003e\u003e, #{codecs =\u003e [datetime]}).\n{{1970,1,1},{0,0,0}}\n```\n\n##### Decode ipv4\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"0.0.0.0\\\"\"\u003e\u003e, #{codecs =\u003e [ipv4]}).\n{0,0,0,0}\n```\n\n##### Decode ipv6\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"::\\\"\"\u003e\u003e, #{codecs =\u003e [ipv6]}).\n{0,0,0,0,0,0,0,0}\n\u003e euneus:decode(\u003c\u003c\"\\\"::1\\\"\"\u003e\u003e, #{codecs =\u003e [ipv6]}).\n{0,0,0,0,0,0,0,1}\n\u003e euneus:decode(\u003c\u003c\"\\\"::192.168.42.2\\\"\"\u003e\u003e, #{codecs =\u003e [ipv6]}).\n{0,0,0,0,0,0,49320,10754}\n\u003e euneus:decode(\u003c\u003c\"\\\"fe80::204:acff:fe17:bf38\\\"\"\u003e\u003e, #{codecs =\u003e [ipv6]}).\n{65152,0,0,0,516,44287,65047,48952}\n```\n\n##### Decode pid\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"\u003c0.92.0\u003e\\\"\"\u003e\u003e, #{codecs =\u003e [pid]}).\n\u003c0.92.0\u003e\n```\n\n##### Decode port\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"#Port\u003c0.1\u003e\\\"\"\u003e\u003e, #{codecs =\u003e [port]}).\n#Port\u003c0.1\u003e\n```\n\n##### Decode reference\n\n```erlang\n\u003e euneus:decode(\u003c\u003c\"\\\"#Ref\u003c0.314572725.1088159747.110918\u003e\\\"\"\u003e\u003e, #{codecs =\u003e [reference]}).\n#Ref\u003c0.314572725.1088159747.110918\u003e\n```\n\n## Installation\n\n### Erlang\n\n```erlang\n% rebar.config\n{deps, [\n    {json_polyfill, \"~\u003e 0.2\"}, % Required only for OTP \u003c 27\n    {euneus, \"~\u003e 2.4\"}\n]}.\n```\n\n### Elixir\n\n```elixir\n# mix.exs\ndefp deps do\n  [\n    {:json_polyfill, \"~\u003e 0.2\"}, # Required only for OTP \u003c 27\n    {:euneus, \"~\u003e 2.4\"}\n  ]\nend\n```\n\n## Basic usage\n\n```erlang\n1\u003e euneus:encode(#{age =\u003e 68, name =\u003e \u003c\u003c\"Joe Armstrong\"\u003e\u003e, nationality =\u003e \u003c\u003c\"British\"\u003e\u003e}).\n\u003c\u003c\"{\\\"name\\\":\\\"Joe Armstrong\\\",\\\"age\\\":68,\\\"nationality\\\":\\\"British\\\"}\"\u003e\u003e\n2\u003e euneus:decode(v(1)).\n#{\u003c\u003c\"age\"\u003e\u003e =\u003e 68,\u003c\u003c\"name\"\u003e\u003e =\u003e \u003c\u003c\"Joe Armstrong\"\u003e\u003e,\u003c\u003c\"nationality\"\u003e\u003e =\u003e \u003c\u003c\"British\"\u003e\u003e}\n```\n\n## Encode\n\nThe functions `euneus:encode/1` `euneus:encode/2` encodes an Erlang term into a binary JSON.\nThe second argument of `euneus:encode/2` are options.\n\nPlease see the `m:euneus_encoder` [documentation](https://hexdocs.pm/euneus/euneus_encoder.html)\nfor more examples and detailed explanation.\n\nThe data mapping and error reasons can be found in the OTP json encode function [documentation](https://erlang.org/documentation/doc-15.0-rc3/lib/stdlib-6.0/doc/html/json.html#encode/1).\n\n## Decode\n\nThe functions `euneus:decode/1` and `euneus:decode/2` decodes a binary JSON into an Erlang term.\nThe second argument of `euneus:decode/2` are options.\n\nPlease see the `m:euneus_decoder` [documentation](https://hexdocs.pm/euneus/euneus_decoder.html)\nfor more examples and detailed explanation.\n\nThe data mapping and error reasons can be found in the OTP json decode function [documentation](https://erlang.org/documentation/doc-15.0-rc3/lib/stdlib-6.0/doc/html/json.html#decode/1).\n\n## Stream\n\nThree functions provide JSON decode streaming:\n\n- `euneus:decode_stream_start/1` - Equivalent to `euneus:decode_stream_start(JSON, #{})`;\n- `euneus:decode_stream_start/2` - Begin parsing a stream of bytes of a JSON value;\n- `euneus:decode_stream_continue/2` - Continue parsing a stream of bytes of a JSON value.\n\nPlease see the `m:euneus_decoder` [documentation](https://hexdocs.pm/euneus/euneus_decoder.html)\nfor more examples and detailed explanation.\n\n## Format\n\nTwo functions provide JSON formatting:\n\n- `euneus:minify/1` - Removes any extra spaces and new line characters;\n- `euneus:format/2` - Formats the JSON by passing custom options.\n\nPlease see the `m:euneus_formatter` [documentation](https://hexdocs.pm/euneus/euneus_formatter.html)\nfor more examples and detailed explanation.\n\n## Benchmark\n\nThe benchmarks are implemented very simply, but they are a good start foroptimizing\nEuneus since no optimizations have been made. You will find the benchmark commands\nin `euneus_benchmarker`, and data and tests under the test folder.\n\n\u003e [!IMPORTANT]\n\u003e For the first benchmark run, bootstrapping `erlperf` is required:\n\u003e\n\u003e ```console\n\u003e $ rebar3 as benchmark shell\n\u003e\n\u003e 1\u003e euneus_benchmarker:bootstrap().\n\u003e ===\u003e Verifying dependencies...\n\u003e ===\u003e Analyzing applications...\n\u003e ===\u003e Compiling erlperf\n\u003e ===\u003e Building escript for erlperf...\n\u003e ok\n\u003e ```\n\n### Results\n\n\u003e Setup:\n\u003e\n\u003e - OS : Linux\n\u003e - CPU: 12th Gen Intel(R) Core(TM) i9-12900HX\n\u003e - VM : Erlang/OTP 27 [erts-15.0.1] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]\n\n```console\n$ rebar3 as benchmark shell\n\n1\u003e euneus_benchmarker:encode_benchmark().\nCode      ||   Samples       Avg   StdDev    Median      P99  Iteration    Rel\njiffy      1         3        26   36.69%        25       36   38474 us   100%\neuneus     1         3        20   38.20%        18       29   49197 us    78%\nthoas      1         3        10   36.06%         9       14     100 ms    38%\n\n2\u003e euneus_benchmarker:decode_benchmark().\nCode       ||   Samples       Avg   StdDev    Median      P99  Iteration    Rel\neuneus      1         3        24    2.44%        24       24   42268 us   100%\njiffy       1         3        19    3.09%        19       19   53589 us    79%\nthoas       1         3        14    0.00%        14       14   71452 us    59%\n```\n\n## Sponsors\n\nIf you like this tool, please consider [sponsoring me](https://github.com/sponsors/williamthome).\nI'm thankful for your never-ending support :heart:\n\nI also accept coffees :coffee:\n\n[![\"Buy Me A Coffee\"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/williamthome)\n\n## License\n\nCopyright (c) 2024 [William Fank Thomé](https://github.com/williamthome)\n\nEuneus is 100% open-source and community-driven. All components are\navailable under the Apache 2 License on [GitHub](https://github.com/williamthome/euneus).\n\nSee [LICENSE.md](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamthome%2Feuneus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwilliamthome%2Feuneus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamthome%2Feuneus/lists"}