{"id":21206187,"url":"https://github.com/rtmigo/jsontree_dart","last_synced_at":"2026-05-01T08:32:46.211Z","repository":{"id":41334060,"uuid":"509150166","full_name":"rtmigo/jsontree_dart","owner":"rtmigo","description":"Statically typed JSON tree","archived":false,"fork":false,"pushed_at":"2022-07-07T15:51:22.000Z","size":82,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"staging","last_synced_at":"2026-04-16T16:22:45.889Z","etag":null,"topics":["dart","data","dynamic","flutter","json","library","package","static","storage","typing"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/jsontree","language":"Dart","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/rtmigo.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-06-30T16:14:54.000Z","updated_at":"2022-10-22T07:24:57.000Z","dependencies_parsed_at":"2022-08-22T15:10:26.032Z","dependency_job_id":null,"html_url":"https://github.com/rtmigo/jsontree_dart","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/rtmigo/jsontree_dart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fjsontree_dart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fjsontree_dart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fjsontree_dart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fjsontree_dart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rtmigo","download_url":"https://codeload.github.com/rtmigo/jsontree_dart/tar.gz/refs/heads/staging","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fjsontree_dart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32490810,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"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":["dart","data","dynamic","flutter","json","library","package","static","storage","typing"],"created_at":"2024-11-20T20:54:44.422Z","updated_at":"2026-05-01T08:32:46.195Z","avatar_url":"https://github.com/rtmigo.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Generic badge](https://img.shields.io/badge/dart-2.17+-blue.svg)\n![Generic badge](https://img.shields.io/badge/platform-VM_|_JS-blue.svg)\n[![Pub Package](https://img.shields.io/pub/v/jsontree.svg)](https://pub.dev/packages/jsontree)\n[![pub points](https://badges.bar/jsontree/pub%20points)](https://pub.dev/packages/jsontree/score)\n\n# [jsontree](https://github.com/rtmigo/jsontree_dart)\n\nStatically typed JSON tree.\n\nThe tree can contain JSON-compatible atomic types and nothing else. That is,\nonly `String`, `int`, `double`, `bool` or `null` -- combined by nested `Map` or\n`List` objects.\n\nThis allows you to prevent data errors at a very early stage. You will see\nwarnings from IDE and the program will not compile.\n\n## Example\n\nCreate a tree in declarative style:\n\n```dart\nimport 'package:jsontree/jsontree.dart';\n\nvoid main() {\n  final tree = {\n    \"planet\": \"Mars\".jsonNode,\n    \"diameter\": 6779.jsonNode,\n    \"satellites\": [\"Phobos\".jsonNode, \"Deimos\".jsonNode].jsonNode\n  }.jsonNode;\n\n  print(tree.toJsonCode());\n  // {\"planet\":\"Mars\",\"diameter\":6779,\"satellites\":[\"Phobos\",\"Deimos\"]}\n}\n```\n\nOr create the tree in an imperative style:\n\n```dart\nimport 'package:jsontree/jsontree.dart';\n\nvoid main() {\n  final satellites = MutableJsonList.empty();\n  satellites.data.add(\"Phobos\".jsonNode);\n  satellites.data.add(\"Deimos\".jsonNode);\n\n  final tree = MutableJsonMap.empty();\n  tree.data[\"planet\"] = \"Mars\".jsonNode;\n  tree.data[\"diameter\"] = 6779.jsonNode;\n  tree.data[\"satellites\"] = satellites;\n\n  print(tree.toJsonCode());\n  // {\"planet\":\"Mars\",\"diameter\":6779,\"satellites\":[\"Phobos\",\"Deimos\"]}\n}\n```\n\n## Motivation\n\nImagine that we need to create some JSON `request`, that will be later converted\nto JSON and sent to server.\n\n#### BAD: dynamic typing\n\n```dart\nimport 'dart:convert';\n\nmain() {\n  final request = \u003cString, dynamic\u003e{};  // to be converted to JSON\n\n  // DateTime is not convertible, but we don't know that yet\n  request[\"time\"] = DateTime.now();  // oops  \n  request[\"message\"] = \"Hi!\";\n\n  // runtime exception: DateTime cannot be converted\n  send(json.convert(request));\n}\n```\n\n#### GOOD: static typing\n\n``` dart\nimport 'dart:convert';\nimport 'package:jsontree/jsontree.dart';\n\nrespond() {\n  final request = MutableJsonMap();  // no dynamic types\n\n  // to place an object inside MutableJsonMap we are forced to convert each \n  // parameter to a JsonNode. But there's no way to convert DateTime to it,\n  // so we have to do it right\n  request[\"time\"] = DateTime.now().millisecondsSinceEpoch.jsonNode;  \n  request[\"message\"] = \"Hi!\".jsonNode;\n\n  // no errors, as it should be\n  send(json.convert(request));\n}\n```\n\n## JsonNode tree creation\n\n```x.jsonNode``` creates an object that wraps the `x` value. The type of the\nobject depends on the type of `x`.\n\nFor example, `5.jsonNode` creates `JsonInt(5)`. And `5.23.jsonNode`\ncreates `JsonDouble(5.23)`.\n\nThis works for collections as well.\n\n``` dart\nfinal sheldon = {\n    'name': 'Sheldon'.jsonNode,\n    'surname': 'Cooper'.jsonNode,\n    'iq': 187.jsonNode,\n    'girlfriends': 1.jsonNode\n}.jsonNode; \n\n// you can't add .jsonNode to the map if you miss at least \n// one .jsonNode added to elements\n\nfinal leonard = {\n    'name': 'Leonard'.jsonNode,\n    'surname': 'Hofstadter'.jsonNode,\n    'iq': 173.jsonNode,\n    'girlfriends': 4.jsonNode\n}.jsonNode;\n\n// connect these nodes into an even larger structure\n\nfinal tree = {\n    'science': 'physics'.jsonNode,\n    'neighbours': [leonard, sheldon].jsonNode\n}.jsonNode; \n```\n\nRegardless of the type, all the wrapper objects will be inherited from the\nbase `JsonNode`. If you have created a `JsonNode`, you can be sure that there is\nJSON-compatible data inside.\n\n## JsonNode tree to JSON string\n\nFor any `JsonNode` object, you can call the `.toJsonCode()` method to convert it\nto JSON string.\n\n``` dart\nimport 'package:jsontree/jsontree.dart';\n...\n\nfinal tree = [1.jsonNode, 2.jsonNode].jsonNode;\nprint(tree.toJsonCode());\n```\n\nYou can also pass the tree directly to `json.convert`:\n\n``` dart\nimport 'package:jsontree/jsontree.dart';\nimport 'dart:convert';\n...\n\nfinal tree = [1.jsonNode, 2.jsonNode].jsonNode;\nprint(json.convert(tree));\n```\n\n## JSON string to JsonNode tree\n\nParsing JSON with this library only makes sense if you want to use the parsed\nvalues to create another tree.\n\n``` dart\nfinal a = JsonNode.fromJsonCode(src1);\nfinal b = JsonNode.fromJsonCode(src2);\n\nprint([a, b, \"something else\".jsonNode].jsonNode.toJsonCode())\n```\n\n\n\n## JsonNode tree to original objects\n\nYou can also call `JsonNode.unwrap()` to get rid of all the wrappers and get the\noriginal set of Dart objects. Because these objects were validated when the tree\nwas created, the result is guaranteed to be able to be converted to JSON.\n\n``` dart\nimport 'package:jsontree/jsontree.dart';\nimport 'dart:convert'\n...\n\nJsonList tree = [1.jsonNode, 2.jsonNode].jsonNode;\nList\u003cint\u003e list = tree.unwrap();  // [1, 2]\n\n// of course, the list convertible to JSON \nprint(json.convert(dartList));\n```\n\n## Objects to JsonNode tree\n\nSuch conversion is contrary to the purpose of the library. It requires dynamic type checking and can lead to runtime errors.\n\nBut if you already have data structures ready, this might be a reasonable compromise.\n\n```dart\nfinal leonard = {\n    'name': 'Leonard',\n    'surname': 'Hofstadter',\n    'iq': 173,\n};\n\nJsonNode tree = JsonNode.wrap(leonard);\n```\n\n\n## JsonNodes immutability\n\nBy default, all objects are immutable.\n\n```dart\nJsonMap m = {\"a\": 1.jsonNode, \"b\": 2.jsonNode}.jsonNode;\n// you can read m or m.data, but cannot change \n```\n\nThere are also mutable versions for lists and maps.\n\n```dart\nvar m = MutableJsonMap({\"a\": 1.jsonNode, \"b\": 2.jsonNode});\n// you can read/write m and m.data \n```\n\nMutability and immutability are achievable after the creation of objects.\n\n``` dart\nJsonMap readOnly = {\"a\": 1.jsonNode, \"b\": 2.jsonNode}.jsonNode;\n\nMutableJsonMap readWrite = readOnly.toMutable();  // creates a copy\nreadWrite[\"c\"] = 3.jsonNode;\n\nJsonMap readOnlyAgain = readWrite.asImmutable();  // wraps the data as immutable\n```\n\n`toMutable` will create a copy of the data, respecting the immutability of\nthe original objects.\n\n`asImmutable` will just wrap the data into an object, that does not allow\nmodification.\n\n\n## Hierarchy\n\n```\nJsonAny\n^^ JsonValue\n   ^^ JsonInt\n      ^^ JsonInt53   (JavaScript range)\n      ^^ JsonInt64   (full int64 range) \n   ^^ JsonDouble\n   ^^ JsonString\n^^ JsonList\n   ^^ MutableJsonList\n^^ JsonMap\n   ^^ MutableJsonMap\n^^ JsonNull\n```\n\nBy default, all the objects are immutable except `MutableJsonMap`\nand `MutableJsonList`.\n\n## Integer ranges\n\nBy default, `int.jsonNode` creates a `JsonInt53` object. It only allows you to\nset integer values that will not lose precision in JavaScript.\n\n```dart\nfinal a = 5.jsonNode;  // no problem\nfinal b = 9999999999999999.jsonNode;  // throws ArgumentError\n```\n\nThis restriction is relevant because JSON is literally JavaScript Object\nNotation.\n\nBut most languages are able to read larger numbers from JSON. To store the full\nrange number, use `int.jsonNode64`.\n\n\n```dart\nfinal c = 9999999999999999.jsonNode64;  // no problem\n```\n\n## License\n\nCopyright © 2022 [Artёm IG](https://github.com/rtmigo).\nReleased under the [MIT License](LICENSE).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtmigo%2Fjsontree_dart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frtmigo%2Fjsontree_dart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtmigo%2Fjsontree_dart/lists"}