{"id":13850152,"url":"https://github.com/jamesmacaulay/elm-json-bidirectional","last_synced_at":"2025-10-20T03:17:15.613Z","repository":{"id":62418582,"uuid":"90226807","full_name":"jamesmacaulay/elm-json-bidirectional","owner":"jamesmacaulay","description":"Helps you construct two-way JSON encoder-decoders in Elm.","archived":false,"fork":false,"pushed_at":"2017-05-18T18:46:09.000Z","size":24,"stargazers_count":10,"open_issues_count":2,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-01T07:23:08.646Z","etag":null,"topics":["elm","json","json-decoding","json-encoding","json-serialization"],"latest_commit_sha":null,"homepage":"http://package.elm-lang.org/packages/jamesmacaulay/elm-json-bidirectional/latest","language":"Elm","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamesmacaulay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-04T05:56:23.000Z","updated_at":"2020-12-16T01:29:29.000Z","dependencies_parsed_at":"2022-11-01T16:46:22.266Z","dependency_job_id":null,"html_url":"https://github.com/jamesmacaulay/elm-json-bidirectional","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesmacaulay%2Felm-json-bidirectional","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesmacaulay%2Felm-json-bidirectional/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesmacaulay%2Felm-json-bidirectional/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesmacaulay%2Felm-json-bidirectional/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamesmacaulay","download_url":"https://codeload.github.com/jamesmacaulay/elm-json-bidirectional/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238535851,"owners_count":19488605,"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":["elm","json","json-decoding","json-encoding","json-serialization"],"created_at":"2024-08-04T20:01:00.172Z","updated_at":"2025-10-20T03:17:15.527Z","avatar_url":"https://github.com/jamesmacaulay.png","language":"Elm","funding_links":[],"categories":["Elm"],"sub_categories":[],"readme":"# jamesmacaulay/elm-json-bidirectional\n\nThis package lets you build up two-way `Coder` structures that concisely specify how values of some Elm type can be both encoded to and decoded from JSON.\n\n## Why?\n\nIf you frequently encode from and decode to the same Elm types, it can be tedious and error prone to define your encoders and decoders separately:\n\n```elm\nimport Json.Encode as Encode\nimport Json.Decode as Decode exposing (Decoder)\n\ntype alias User =\n    { name : String\n    , isAdmin : Bool\n    }\n\nuserDecoder : Decoder User\nuserDecoder =\n    Decode.map2 User\n        (Decode.field \"name\" Decode.string)\n        (Decode.field \"isAdmin\" Decode.bool)\n\nencodeUser : User -\u003e Encode.Value\nencodeUser user =\n    Encode.object\n        [ ( \"name\", Encode.string user.name )\n        , ( \"isAdmin\", Encode.bool user.isAdmin )\n        ]\n```\n\nIf you're encoding and decoding a lot of different kinds of data, this requires a lot of code in different functions that needs to be kept in sync. If you add a field to one of your record types but forget to add it to the type's encoder, the compiler can't help you find the omission and you might end up with bad data that can't be decoded with the corresponding decoder. [Fuzz tests work well to prevent this](https://www.brianthicks.com/post/2017/04/24/add-safety-to-your-elm-json-encoders-with-fuzz-testing/), but they require yet more code to be written to remedy the problem.\n\nWith this package, you can instead build up a single `Coder` that knows how to both encode and decode:\n\n```elm\nimport Json.Bidirectional as Json\n\ntype alias User =\n    { name : String\n    , isAdmin : Bool\n    }\n\nuserCoder : Json.Coder User\nuserCoder =\n    Json.object User\n        |\u003e Json.withField \"name\" .name Json.string\n        |\u003e Json.withField \"isAdmin\" .isAdmin Json.bool\n```\n\nEncoding and decoding is accomplished with the `encodeValue`, `encodeString`, `decodeValue`, and `decodeString` functions. If you want to get a Decoder from a Coder, you can use the `decoder` function.\n\n## What's the catch?\n\nBecause of the nature of the encoding and decoding processes, this approach is not so great if you are working with JSON that is of a very different structure than its corresponding Elm types.\n\nAlso, specifying a bidirectional `Coder` for union types with more than one constructor is a bit of a hassle (see the `custom` function for an example).\n\n## When should I use this package then?\n\nThis package is at its best when you have full control over the shape of the JSON that you're encoding and decoding from.\n\n## What about fuzz tests?\n\nFuzz tests are a great way to make sure your encoders and decoders are mirror images of each other. Here's a great article on this topic that was also the inspiration for releasing this package:\n\nhttps://www.brianthicks.com/post/2017/04/24/add-safety-to-your-elm-json-encoders-with-fuzz-testing/\n\nIf you use this package to build bidirectional Coders, you won't need as many fuzz tests to ensure consistency, but you will still want them in some cases where it's possible to make mistakes. The Elm compiler will ensure that values encoded by a Coder will be able to be decoded to the original type by the same Coder, but the type system cannot always guarantee that the decoded _value_ will be identical to the original. Listed below are some ways that you can make asymmetrical Coders with this package if you aren't careful. These are situations where you might decide that fuzz tests are still worthwhile.\n\n## Ordering object fields\n\nOne way that the encoded and decoded values might not be equal is if you specify object fields out of order. For example:\n\n```elm\nimport Json.Bidirectional as Json\n\ntype alias EmailContact =\n    { name : String\n    , email : String\n    }\n\nemailContactCoder : Json.Coder EmailContact\nemailContactCoder =\n    Json.object EmailContact\n        -- fields in the wrong order!\n        |\u003e Json.withField \"email\" .email Json.string\n        |\u003e Json.withField \"name\" .name Json.string\n```\n\nThe above Coder will encode `{ name = \"Alice\", email = \"alice@example.com\" }` correctly as `{\"name\": \"Alice\", \"email\": \"alice@example.com\"}`. However, because the two string fields are specified in the wrong order, the EmailContact constructor decodes the `\"email\"` field as its `name` and vice-versa.\n\n## bimap\n\nThe `bimap` function lets you map both the encoding and decoding processes of a Coder by supplying one function for each direction. Here's a contrived example:\n\n```elm\nimport Json.Bidirectional as Json\n\ntype StringPair\n    = StringPair String String\n\nstringPairCoder : Json.Coder StringPair\nstringPairCoder =\n    Json.tuple (Json.string, Json.string)\n        |\u003e Json.bimap\n            (\\(StringPair left right) -\u003e (left, right))\n            (\\(left, right) -\u003e StringPair left right)\n```\n\nThese mapping functions are just complex enough that you might make a mistake in the implementation:\n\n```elm\ninconsistentStringPairCoder : Json.Coder StringPair\ninconsistentStringPairCoder =\n    Json.tuple (Json.string, Json.string)\n        |\u003e Json.bimap\n            -- the left String is used in both places in the encoding!\n            (\\(StringPair left right) -\u003e (left, left))\n            (\\(left, right) -\u003e StringPair left right)\n```\n\n## custom\n\nThe `custom` function lets you create an arbitrary Coder for any type by supplying an encoding function and Decoder for a single type. This function is most useful for implementing Coders for union types with multiple constructors. Use of the `custom` function in this way tends to be the most complex and error-prone way of constructing a Coder that this package makes available, and so fuzz testing `custom` Coders is highly recommended.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesmacaulay%2Felm-json-bidirectional","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamesmacaulay%2Felm-json-bidirectional","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesmacaulay%2Felm-json-bidirectional/lists"}