Last updated:
0 purchases
jsontree
jsontree #
Statically typed JSON tree.
The tree can contain JSON-compatible atomic types and nothing else. That is,
only String, int, double, bool or null -- combined by nested Map or
List objects.
This allows you to prevent data errors at a very early stage. You will see
warnings from IDE and the program will not compile.
Example #
Create a tree in declarative style:
import 'package:jsontree/jsontree.dart';
void main() {
final tree = {
"planet": "Mars".jsonNode,
"diameter": 6779.jsonNode,
"satellites": ["Phobos".jsonNode, "Deimos".jsonNode].jsonNode
}.jsonNode;
print(tree.toJsonCode());
// {"planet":"Mars","diameter":6779,"satellites":["Phobos","Deimos"]}
}
copied to clipboard
Or create the tree in an imperative style:
import 'package:jsontree/jsontree.dart';
void main() {
final satellites = MutableJsonList.empty();
satellites.data.add("Phobos".jsonNode);
satellites.data.add("Deimos".jsonNode);
final tree = MutableJsonMap.empty();
tree.data["planet"] = "Mars".jsonNode;
tree.data["diameter"] = 6779.jsonNode;
tree.data["satellites"] = satellites;
print(tree.toJsonCode());
// {"planet":"Mars","diameter":6779,"satellites":["Phobos","Deimos"]}
}
copied to clipboard
Motivation #
Imagine that we need to create some JSON request, that will be later converted
to JSON and sent to server.
BAD: dynamic typing
import 'dart:convert';
main() {
final request = <String, dynamic>{}; // to be converted to JSON
// DateTime is not convertible, but we don't know that yet
request["time"] = DateTime.now(); // oops
request["message"] = "Hi!";
// runtime exception: DateTime cannot be converted
send(json.convert(request));
}
copied to clipboard
GOOD: static typing
import 'dart:convert';
import 'package:jsontree/jsontree.dart';
respond() {
final request = MutableJsonMap(); // no dynamic types
// to place an object inside MutableJsonMap we are forced to convert each
// parameter to a JsonNode. But there's no way to convert DateTime to it,
// so we have to do it right
request["time"] = DateTime.now().millisecondsSinceEpoch.jsonNode;
request["message"] = "Hi!".jsonNode;
// no errors, as it should be
send(json.convert(request));
}
copied to clipboard
JsonNode tree creation #
x.jsonNode creates an object that wraps the x value. The type of the
object depends on the type of x.
For example, 5.jsonNode creates JsonInt(5). And 5.23.jsonNode
creates JsonDouble(5.23).
This works for collections as well.
final sheldon = {
'name': 'Sheldon'.jsonNode,
'surname': 'Cooper'.jsonNode,
'iq': 187.jsonNode,
'girlfriends': 1.jsonNode
}.jsonNode;
// you can't add .jsonNode to the map if you miss at least
// one .jsonNode added to elements
final leonard = {
'name': 'Leonard'.jsonNode,
'surname': 'Hofstadter'.jsonNode,
'iq': 173.jsonNode,
'girlfriends': 4.jsonNode
}.jsonNode;
// connect these nodes into an even larger structure
final tree = {
'science': 'physics'.jsonNode,
'neighbours': [leonard, sheldon].jsonNode
}.jsonNode;
copied to clipboard
Regardless of the type, all the wrapper objects will be inherited from the
base JsonNode. If you have created a JsonNode, you can be sure that there is
JSON-compatible data inside.
JsonNode tree to JSON string #
For any JsonNode object, you can call the .toJsonCode() method to convert it
to JSON string.
import 'package:jsontree/jsontree.dart';
...
final tree = [1.jsonNode, 2.jsonNode].jsonNode;
print(tree.toJsonCode());
copied to clipboard
You can also pass the tree directly to json.convert:
import 'package:jsontree/jsontree.dart';
import 'dart:convert';
...
final tree = [1.jsonNode, 2.jsonNode].jsonNode;
print(json.convert(tree));
copied to clipboard
JSON string to JsonNode tree #
Parsing JSON with this library only makes sense if you want to use the parsed
values to create another tree.
final a = JsonNode.fromJsonCode(src1);
final b = JsonNode.fromJsonCode(src2);
print([a, b, "something else".jsonNode].jsonNode.toJsonCode())
copied to clipboard
JsonNode tree to original objects #
You can also call JsonNode.unwrap() to get rid of all the wrappers and get the
original set of Dart objects. Because these objects were validated when the tree
was created, the result is guaranteed to be able to be converted to JSON.
import 'package:jsontree/jsontree.dart';
import 'dart:convert'
...
JsonList tree = [1.jsonNode, 2.jsonNode].jsonNode;
List<int> list = tree.unwrap(); // [1, 2]
// of course, the list convertible to JSON
print(json.convert(dartList));
copied to clipboard
Objects to JsonNode tree #
Such conversion is contrary to the purpose of the library. It requires dynamic type checking and can lead to runtime errors.
But if you already have data structures ready, this might be a reasonable compromise.
final leonard = {
'name': 'Leonard',
'surname': 'Hofstadter',
'iq': 173,
};
JsonNode tree = JsonNode.wrap(leonard);
copied to clipboard
JsonNodes immutability #
By default, all objects are immutable.
JsonMap m = {"a": 1.jsonNode, "b": 2.jsonNode}.jsonNode;
// you can read m or m.data, but cannot change
copied to clipboard
There are also mutable versions for lists and maps.
var m = MutableJsonMap({"a": 1.jsonNode, "b": 2.jsonNode});
// you can read/write m and m.data
copied to clipboard
Mutability and immutability are achievable after the creation of objects.
JsonMap readOnly = {"a": 1.jsonNode, "b": 2.jsonNode}.jsonNode;
MutableJsonMap readWrite = readOnly.toMutable(); // creates a copy
readWrite["c"] = 3.jsonNode;
JsonMap readOnlyAgain = readWrite.asImmutable(); // wraps the data as immutable
copied to clipboard
toMutable will create a copy of the data, respecting the immutability of
the original objects.
asImmutable will just wrap the data into an object, that does not allow
modification.
Hierarchy #
JsonAny
^^ JsonValue
^^ JsonInt
^^ JsonInt53 (JavaScript range)
^^ JsonInt64 (full int64 range)
^^ JsonDouble
^^ JsonString
^^ JsonList
^^ MutableJsonList
^^ JsonMap
^^ MutableJsonMap
^^ JsonNull
copied to clipboard
By default, all the objects are immutable except MutableJsonMap
and MutableJsonList.
Integer ranges #
By default, int.jsonNode creates a JsonInt53 object. It only allows you to
set integer values that will not lose precision in JavaScript.
final a = 5.jsonNode; // no problem
final b = 9999999999999999.jsonNode; // throws ArgumentError
copied to clipboard
This restriction is relevant because JSON is literally JavaScript Object
Notation.
But most languages are able to read larger numbers from JSON. To store the full
range number, use int.jsonNode64.
final c = 9999999999999999.jsonNode64; // no problem
copied to clipboard
License #
Copyright © 2022 Artёm IG.
Released under the MIT License.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.