{"id":41552702,"url":"https://github.com/lostbean/json_blueprint","last_synced_at":"2026-01-24T04:45:49.014Z","repository":{"id":265511802,"uuid":"896149869","full_name":"lostbean/json_blueprint","owner":"lostbean","description":"Blueprint is a Gleam library that simplifies JSON encoding and decoding while automatically generating JSON schemas for your data types.","archived":false,"fork":false,"pushed_at":"2025-03-30T01:26:27.000Z","size":84,"stargazers_count":15,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-17T01:58:00.622Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Gleam","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/lostbean.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-29T16:42:37.000Z","updated_at":"2025-08-14T13:04:52.000Z","dependencies_parsed_at":"2024-11-29T18:30:02.782Z","dependency_job_id":null,"html_url":"https://github.com/lostbean/json_blueprint","commit_stats":null,"previous_names":["lostbean/json_blueprint","lostbean/blueprint"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lostbean/json_blueprint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lostbean%2Fjson_blueprint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lostbean%2Fjson_blueprint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lostbean%2Fjson_blueprint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lostbean%2Fjson_blueprint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lostbean","download_url":"https://codeload.github.com/lostbean/json_blueprint/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lostbean%2Fjson_blueprint/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28711964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T23:51:44.727Z","status":"online","status_checked_at":"2026-01-24T02:00:06.909Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-01-24T04:45:48.183Z","updated_at":"2026-01-24T04:45:49.009Z","avatar_url":"https://github.com/lostbean.png","language":"Gleam","funding_links":[],"categories":[],"sub_categories":[],"readme":"# json_blueprint\n\njson_blueprint is a Gleam library that simplifies JSON encoding and decoding while automatically generating JSON schemas for your data types.\n\n[![Package Version](https://img.shields.io/hexpm/v/json_blueprint)](https://hex.pm/packages/json_blueprint)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/json_blueprint/)\n\n```sh\ngleam add json_blueprint\n```\n\n## Usage\n\njson_blueprint provides utilities for encoding and decoding JSON data, with special support for union types. The generated JSON schemas can be used to validate incoming JSON data with the decoder. The JSON schema follows the [JSON Schema Draft 7](https://json-schema.org/) specification and can tested and validate on [JSON Schema Lint](https://jsonschemalint.com/#!/version/draft-07/markup/json).\n\n\u003e ❗️ _**IMPORTANT: Recursive data types**_\n\u003e\n\u003e Make to use the `self_decoder` when defining the decoder for recursive data types.\n\n\u003e ⚠️ _**WARNING: Do NOT use on cyclical data type definitions**_\n\u003e\n\u003e While the library supports recursive data types (types with self reference), it does not support cyclical data types (cyclical dependency between multiple data types). Cyclical data types will result in infinite loop during decoding or schema generation.\n\n## Examples\n\n\u003cdetails\u003e\n  \u003csummary\u003eEncoding Union Types\u003c/summary\u003e\n  \nHere's an example of encoding a union type to JSON:\n\n```gleam\nimport gleam/io\nimport gleam/json\nimport gleeunit\nimport gleeunit/should\nimport json/blueprint\n\npub fn main() {\n  gleeunit.main()\n}\n\ntype Shape {\n  Circle(Float)\n  Rectangle(Float, Float)\n  Void\n}\n\nfn encode_shape(shape: Shape) -\u003e json.Json {\n  blueprint.union_type_encoder(shape, fn(shape_case) {\n    case shape_case {\n      Circle(radius) -\u003e #(\n        \"circle\",\n        json.object([#(\"radius\", json.float(radius))]),\n      )\n      Rectangle(width, height) -\u003e #(\n        \"rectangle\",\n        json.object([\n          #(\"width\", json.float(width)),\n          #(\"height\", json.float(height)),\n        ]),\n      )\n      Void -\u003e #(\"void\", json.object([]))\n    }\n  })\n}\n\nfn shape_decoder() -\u003e blueprint.Decoder(Shape) {\n  blueprint.union_type_decoder([\n    #(\n      \"circle\",\n      blueprint.decode1(Circle, blueprint.field(\"radius\", blueprint.float())),\n    ),\n    #(\n      \"rectangle\",\n      blueprint.decode2(\n        Rectangle,\n        blueprint.field(\"width\", blueprint.float()),\n        blueprint.field(\"height\", blueprint.float()),\n      ),\n    ),\n    #(\"void\", blueprint.decode0(Void)),\n  ])\n}\n\npub fn union_type_test() {\n  let circle = Circle(5.0)\n  let rectangle = Rectangle(10.0, 20.0)\n\n  let decoder = shape_decoder()\n\n  //test decoding\n  encode_shape(circle)\n  |\u003e json.to_string\n  |\u003e blueprint.decode(using: decoder)\n  |\u003e should.equal(Ok(circle))\n\n  encode_shape(rectangle)\n  |\u003e json.to_string\n  |\u003e blueprint.decode(using: decoder)\n  |\u003e should.equal(Ok(rectangle))\n\n  encode_shape(Void)\n  |\u003e json.to_string\n  |\u003e blueprint.decode(using: decoder)\n  |\u003e should.equal(Ok(Void))\n\n  blueprint.generate_json_schema(shape_decoder())\n  |\u003e json.to_string\n  |\u003e io.println\n}\n```\n\n#### Generated JSON Schema\n\n```json\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"anyOf\": [\n    {\n      \"required\": [\"type\", \"data\"],\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": {\n          \"type\": \"string\",\n          \"enum\": [\"circle\"]\n        },\n        \"data\": {\n          \"required\": [\"radius\"],\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"radius\": {\n              \"type\": \"number\"\n            }\n          }\n        }\n      }\n    },\n    {\n      \"required\": [\"type\", \"data\"],\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": {\n          \"type\": \"string\",\n          \"enum\": [\"rectangle\"]\n        },\n        \"data\": {\n          \"required\": [\"width\", \"height\"],\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"width\": {\n              \"type\": \"number\"\n            },\n            \"height\": {\n              \"type\": \"number\"\n            }\n          }\n        }\n      }\n    },\n    {\n      \"required\": [\"type\", \"data\"],\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": {\n          \"type\": \"string\",\n          \"enum\": [\"void\"]\n        },\n        \"data\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {}\n        }\n      }\n    }\n  ]\n}\n```\n\nThis will encode your union types into a standardized JSON format with `type` and `data` fields, making it easy to decode on the receiving end.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eType aliases and optional fields\u003c/summary\u003e\n  \nAnd here's an example using type aliases, optional fields, and single constructor types:\n\n```gleam\nimport gleam/io\nimport gleam/json\nimport gleam/option.{type Option, None, Some}\nimport gleeunit\nimport gleeunit/should\nimport json/blueprint\n\npub fn main() {\n  gleeunit.main()\n}\n\ntype Color {\n  Red\n  Green\n  Blue\n}\n\ntype Coordinate =\n  #(Float, Float)\n\ntype Drawing {\n  Box(Float, Float, Coordinate, Option(Color))\n}\n\nfn color_decoder() {\n  blueprint.enum_type_decoder([\n    #(\"red\", Red),\n    #(\"green\", Green),\n    #(\"blue\", Blue),\n  ])\n}\n\nfn color_encoder(input) {\n  blueprint.enum_type_encoder(input, fn(color) {\n    case color {\n      Red -\u003e \"red\"\n      Green -\u003e \"green\"\n      Blue -\u003e \"blue\"\n    }\n  })\n}\n\nfn encode_coordinate(coord: Coordinate) -\u003e json.Json {\n  blueprint.encode_tuple2(coord, json.float, json.float)\n}\n\nfn coordinate_decoder() {\n  blueprint.tuple2(blueprint.float(), blueprint.float())\n}\n\nfn encode_drawing(drawing: Drawing) -\u003e json.Json {\n  blueprint.union_type_encoder(drawing, fn(shape) {\n    case shape {\n      Box(width, height, position, color) -\u003e #(\n        \"box\",\n        json.object([\n          #(\"width\", json.float(width)),\n          #(\"height\", json.float(height)),\n          #(\"position\", encode_coordinate(position)),\n          #(\"color\", json.nullable(color, color_encoder)),\n        ]),\n      )\n    }\n  })\n}\n\nfn drawing_decoder() -\u003e blueprint.Decoder(Drawing) {\n  blueprint.union_type_decoder([\n    #(\n      \"box\",\n      blueprint.decode4(\n        Box,\n        blueprint.field(\"width\", blueprint.float()),\n        blueprint.field(\"height\", blueprint.float()),\n        blueprint.field(\"position\", coordinate_decoder()),\n        blueprint.optional_field(\"color\", color_decoder()),\n      ),\n    ),\n  ])\n}\n\npub fn drawing_test() {\n  // Test cases\n  let box = Box(15.0, 25.0, #(30.0, 40.0), None)\n\n  // Test encoding\n  let encoded_box = encode_drawing(box)\n\n  // Test decoding\n  encoded_box\n  |\u003e json.to_string\n  |\u003e blueprint.decode(using: drawing_decoder())\n  |\u003e should.equal(Ok(box))\n\n  blueprint.generate_json_schema(drawing_decoder())\n  |\u003e json.to_string\n  |\u003e io.println\n}\n\n```\n\n#### Generated JSON Schema\n\n```json\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"required\": [\"type\", \"data\"],\n  \"additionalProperties\": false,\n  \"type\": \"object\",\n  \"properties\": {\n    \"type\": {\n      \"type\": \"string\",\n      \"enum\": [\"box\"]\n    },\n    \"data\": {\n      \"required\": [\"width\", \"height\", \"position\"],\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"width\": {\n          \"type\": \"number\"\n        },\n        \"height\": {\n          \"type\": \"number\"\n        },\n        \"position\": {\n          \"maxItems\": 2,\n          \"minItems\": 2,\n          \"prefixItems\": [\n            {\n              \"type\": \"number\"\n            },\n            {\n              \"type\": \"number\"\n            }\n          ],\n          \"type\": \"array\"\n        },\n        \"color\": {\n          \"required\": [\"enum\"],\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"enum\": {\n              \"type\": \"string\",\n              \"enum\": [\"red\", \"green\", \"blue\"]\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eRecursive data types\u003c/summary\u003e\n  \nAnd here's an example using type aliases, optional fields, and single constructor types:\n\n```gleam\nimport gleam/io\nimport gleam/json\nimport gleam/option.{type Option, None, Some}\nimport gleeunit\nimport gleeunit/should\nimport json/blueprint\n\npub fn main() {\n  gleeunit.main()\n}\n\ntype Tree {\n  Node(value: Int, left: Option(Tree), right: Option(Tree))\n}\n\ntype ListOfTrees(t) {\n  ListOfTrees(head: t, tail: ListOfTrees(t))\n  NoTrees\n}\n\nfn encode_tree(tree: Tree) -\u003e json.Json {\n  blueprint.union_type_encoder(tree, fn(node) {\n    case node {\n      Node(value, left, right) -\u003e #(\n        \"node\",\n        [\n          #(\"value\", json.int(value)),\n          #(\"right\", json.nullable(right, encode_tree)),\n        ]\n          |\u003e blueprint.encode_optional_field(\"left\", left, encode_tree)\n          |\u003e json.object(),\n      )\n    }\n  })\n}\n\nfn encode_list_of_trees(tree: ListOfTrees(Tree)) -\u003e json.Json {\n  blueprint.union_type_encoder(tree, fn(list) {\n    case list {\n      ListOfTrees(head, tail) -\u003e #(\n        \"list\",\n        json.object([\n          #(\"head\", encode_tree(head)),\n          #(\"tail\", encode_list_of_trees(tail)),\n        ]),\n      )\n      NoTrees -\u003e #(\"no_trees\", json.object([]))\n    }\n  })\n}\n\n// Without reuse_decoder, recursive types would cause infinite schema expansion\nfn tree_decoder() {\n  blueprint.union_type_decoder([\n    #(\n      \"node\",\n      blueprint.decode3(\n        Node,\n        blueprint.field(\"value\", blueprint.int()),\n        // testing both an optional field a field with a possible null\n        blueprint.optional_field(\"left\", blueprint.self_decoder(tree_decoder)),\n        blueprint.field(\n          \"right\",\n          blueprint.optional(blueprint.self_decoder(tree_decoder)),\n        ),\n      ),\n    ),\n  ])\n  // !!!IMPORTANT!!! Add the reuse_decoder when there are nested recursive types so\n  // the schema references (`#`) get rewritten correctly and self-references from the\n  // different types don't get mixed up. As a recommendation, always add it when\n  // decoding recursive types.\n  |\u003e blueprint.reuse_decoder\n}\n\nfn decode_list_of_trees() {\n  blueprint.union_type_decoder([\n    #(\n      \"list\",\n      blueprint.decode2(\n        ListOfTrees,\n        blueprint.field(\"head\", tree_decoder()),\n        blueprint.field(\"tail\", blueprint.self_decoder(decode_list_of_trees)),\n      ),\n    ),\n    #(\"no_trees\", blueprint.decode0(NoTrees)),\n  ])\n}\n\npub fn tree_decoder_test() {\n  // Create a sample tree structure:\n  //       5\n  //      / \\\n  //     3   7\n  //    /     \\\n  //   1       9\n  let tree =\n    Node(\n      value: 5,\n      left: Some(Node(value: 3, left: Some(Node(1, None, None)), right: None)),\n      right: Some(Node(value: 7, left: None, right: Some(Node(9, None, None)))),\n    )\n\n  // Create a list of trees\n  let tree_list =\n    ListOfTrees(\n      Node(value: 1, left: None, right: None),\n      ListOfTrees(\n        Node(\n          value: 10,\n          left: Some(Node(value: 1, left: None, right: None)),\n          right: None,\n        ),\n        NoTrees,\n      ),\n    )\n\n  // Test encoding\n  let json_str = tree |\u003e encode_tree |\u003e json.to_string()\n  let list_json_str = tree_list |\u003e encode_list_of_trees |\u003e json.to_string()\n\n  // Test decoding\n  let decoded = blueprint.decode(using: tree_decoder(), from: json_str)\n\n  decoded\n  |\u003e should.equal(Ok(tree))\n\n  let decoded_list =\n    blueprint.decode(using: decode_list_of_trees(), from: list_json_str)\n\n  decoded_list\n  |\u003e should.equal(Ok(tree_list))\n\n  // Test schema generation\n  blueprint.generate_json_schema(decode_list_of_trees())\n  |\u003e json.to_string\n  |\u003e io.println\n}\n```\n\n#### Generated JSON Schema\n\n```json\n{\n  \"$defs\": {\n    \"ref_CEF475B4CA96DC7B2C0C206AC7598AFFC4B66FD2\": {\n      \"required\": [\"type\", \"data\"],\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": {\n          \"type\": \"string\",\n          \"enum\": [\"node\"]\n        },\n        \"data\": {\n          \"required\": [\"value\", \"right\"],\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"value\": {\n              \"type\": \"integer\"\n            },\n            \"left\": {\n              \"$ref\": \"#/$defs/ref_CEF475B4CA96DC7B2C0C206AC7598AFFC4B66FD2\"\n            },\n            \"right\": {\n              \"anyOf\": [\n                {\n                  \"$ref\": \"#/$defs/ref_CEF475B4CA96DC7B2C0C206AC7598AFFC4B66FD2\"\n                },\n                {\n                  \"type\": \"null\"\n                }\n              ]\n            }\n          }\n        }\n      }\n    }\n  },\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"anyOf\": [\n    {\n      \"required\": [\"type\", \"data\"],\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": {\n          \"type\": \"string\",\n          \"enum\": [\"list\"]\n        },\n        \"data\": {\n          \"required\": [\"head\", \"tail\"],\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {\n            \"head\": {\n              \"$ref\": \"#/$defs/ref_CEF475B4CA96DC7B2C0C206AC7598AFFC4B66FD2\"\n            },\n            \"tail\": {\n              \"$ref\": \"#\"\n            }\n          }\n        }\n      }\n    },\n    {\n      \"required\": [\"type\", \"data\"],\n      \"additionalProperties\": false,\n      \"type\": \"object\",\n      \"properties\": {\n        \"type\": {\n          \"type\": \"string\",\n          \"enum\": [\"no_trees\"]\n        },\n        \"data\": {\n          \"additionalProperties\": false,\n          \"type\": \"object\",\n          \"properties\": {}\n        }\n      }\n    }\n  ]\n}\n```\n\n\u003c/details\u003e\n\n## Features\n\n- 🎯 Type-safe JSON encoding and decoding\n- 🔄 Support for union types with standardized encoding\n- 📋 Automatic JSON schema generation\n- ✨ Clean and intuitive API\n\nFurther documentation can be found at \u003chttps://hexdocs.pm/json_blueprint\u003e.\n\n## Development\n\n```sh\ngleam run   # Run the project\ngleam test  # Run the tests\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flostbean%2Fjson_blueprint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flostbean%2Fjson_blueprint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flostbean%2Fjson_blueprint/lists"}