Last updated:
0 purchases
map list dot
MapList : quick prototyping #
Create pseudo class with accessors from json descriptor #
A json String, a json 'Dart', or any maps & lists set, are enough to create a class with a dot notation to access properties.
dynamic p1 = MapListMap({
"name": "Polo",
"firstName": "marco",
"birthDate": { "day": 15, "month": 9, "year": 1254 }
});
copied to clipboard
use getter in dot notation
print('${p1.firstName} ${p1.name} have now ${DateTime.now().year - p1.birthDate.year} years');
// -> marco Polo have now 766 years
copied to clipboard
can apply setters #
p1.firstName = 'Marco';
copied to clipboard
can dynamically create new named properties #
// add a collection for business cards
p1.cards = [];
// add a new card with a pre-filled map
p1.cards.add({
"mail": "[email protected]",
});
copied to clipboard
Continue freely the creation chain
// add to this -last added- map a new entry
p1.cards.last.phone = "+99 01 02 03 04 05";
print(p1.cards.last.mail);
print(p1.cards.last.phone);
//@see examples for more code
copied to clipboard
At any time the underlying json is updated and available
print(p1.json);
copied to clipboard
Access and update your Data Objects with scripts #
What is available in dot notation within Dart is also available by script :
var scriptLines = [
'persons=[]',
'''persons.add({ "name": "Magellan", "firstName": "Fernando",
"birthDate": { "day": 15,"month": 3,"year": 1480}
})
''',
'persons.last.cards = {"mail": "[email protected]"})',
'persons.last.cards.phone = "+99 01 02 03 04 05"'
];
copied to clipboard
Script executor is under a MapList responsibility
dynamic myKB = MapListMap();
for (String line in scriptLines) myKB.eval(line);
copied to clipboard
Resulting data can be accessed by code or by script
print( myKB.persons.last.cards.phone); // code
print(myKB.eval('persons.last.cards.phone')); // interpreter
copied to clipboard
now some details #
constructors #
There is two kinds of structures : MapListMap and MapListList
If you decide by yourself, choose your root within this two options:
dynamic myRootMap = MapListMap(); // empty map { }
dynamic myRootList = MapListList(); // empty list [ ]
copied to clipboard
If you rely on a variable json , you can leave a higher factory do the choice :
dynamic myRoot = Maplist(someJson); // return a MapListMap or a MapListList
copied to clipboard
constructors with json data #
Each constructor can be default as above, or can be initialised with :
a Json String
an inline maps and lists in dart
an already loaded 'Dart Json'
dynamic myRootMap = MapListMap('{"name":"Polo"}'); // string
dynamic myRootMap = MapListMap({"name":"Polo"}); // inline
var myJson = json.decode('{"name":"Polo"}');
dynamic myRootMap = MapListMap(myJson); // already json dart
copied to clipboard
Same options are for MapListList constructors, but beginning by a List [ ]
Same options with the Factory MapList which will decide of the following.
accessing data #
Classical and dot notation are usable in code and in scripts.
The result is the last leaf which could be a simple data, a List, a Map or a null if not found.
classical notation
root["show"]["videos"][1]["name"]
copied to clipboard
dot notation
root.show.videos[1].name
copied to clipboard
General access
a .someName indicates a key entry in a map.
if the result is another Map
can continue with another key : .someName.someList
if the result is a List
can continue with an index : someList[1]
can use the keyword last : someList.last
The result of the result can be a Map or another List
someList [10].anotherKey
somelist [10] [2]
if the result is a simple data, cannot continue notation : must be the last leaf.
the full name allows to get data : someName.someList[1] // returns an int;
the full name allows to set data : someName.someList[1] = 12;
special words #
Some words are identified as questions or actions.
// on Lists
root.show.videos.length
root.show.videos.clear()
root.show.videos.isEmpty
root.show.videos.isNotEmpty
root.show.videos.last
// on Maps
root.show.length
root.show.clear()
root.show.isEmpty
root.show.isNotEmpty
copied to clipboard
create and set data with dot notation #
create empty structures
dynamic squad = MapListMap(); // create an empty map as squad.
squad.members = []; // add an empty list named 'members'
squad.activities = {}; // add also an empty map 'activities' at first level
dynamic squad = MapListMap({members: [], activities: {}); // the same in one line at construction
copied to clipboard
Note : With dot notation, create unknown data one level at a time (or use json).
create with more pre-filled data
// creation with direct structure
root = MapListMap({"dico":{"hello":{"US": "Hi", "FR": "bonjour"} }});
// If you plan to use heterogeneous data : better to precise type:
root = MapListMap( {"dico":<String,dynamic>{"hello":{"US": "Hi", "FR": "bonjour"} }});
// or use json string message that do the job with its internal types:
root = MapListMap(' {"dico":{"hello":{"US": "Hi", "FR": "bonjour"} }} ');
copied to clipboard
can use relay to simplify access
// follow previous sample : create with more complex data
root.dico.numbers = {"US": ["zero","one","two","three","four","five","sic","seven","eight","nine"]};
var numbers = root.dico.numbers;
var USnumbers = numbers.US;
print(USnumbers[3]);
copied to clipboard
Note : except for the root which must be declared dynamic, you can leave var at lower levels as the returned class is a MapList.
add a Map into another Map (both MapListMap)
dynamic car = MapListMap(); // use dynamic to allow dot notation on root
car.brand = "Ford";
car.colors = ["blue","black","white"];
car.chosenColor = "white";
// now add this car to a larger set
dynamic myStuff = MapListMap();
myStuff.myCar = car; // create a property myCar with given values
copied to clipboard
Add to a List : one element with add, another List with addAll
dynamic list = MapListList();
list.add(15);
list.addAll([1, 2, 3]);
list.add({"date":"october 16"});
print(list); //[15, 1, 2, 3, {date: october 16}]
copied to clipboard
add or change elements of a map with another map : addAll
dynamic car = MapListMap();
car.name = "ford";
// add to this map several key:value in one shot or change existing
car.addAll({ "name":"Ford","price": 5000, "fuel":"diesel","hybrid":false});
copied to clipboard
Check nullable while accessing data #
store.wrongName.someData
copied to clipboard
To avoid the error "NoSuchMethodError: The getter 'someData' was called on null",
Dart takes care of the nullable notation.
The following code will return null or the data, without throwing error.
store.wrongName?.someData
store.eval('wrongName?.someData');
copied to clipboard
note: The interpreter takes care of the null notation.
MapLists return null on unknown data #
unknown key in a Map
wrong index on a List
misused of types , like indexing a Map or using key on a List
for interpreter :
wrong syntax
malformed json
In all cases, MapList will returns null and logs a Warning on the standard logger.
Wrong index on a List : sample of log
MapList logs a Warning with a reminder of the initial error :
print(store.book[400]); // -> null
// WARNING: unexisting book [400] : null returned .
// Original message : RangeError (index): Invalid value: Not in range 0..3, inclusive: 4
copied to clipboard
You can protect downstream errors with a nullable option :
store.book[400]?.author = "zaza" ;
copied to clipboard
Non existing List
If the list doesn't exist at all, the nullable must be checked before the index to avoid error on the operator [ ]:
store.pocketBookList?[0].author = "zaza";
copied to clipboard
Dart allows this syntax recently with Dart 2.9.2.
Before 2.9.2 you cannot compile with a nullable before [0] in code.
The interpreter already allows this syntax.
The hell of data types and how to protect code #
MapList works on a basis of Map<String,dynamic> and List<dynamic> .
Using and adding json data, which are Map<dynamic,dynamic> and List<dynamic>, is full compliant.
warn with inline coded structure
This codes with a List will fail:
root.data = [11, 12, 13]; // will infer a List<int>
root.data.add(14); // ok
root.data.add("hello"); // will fail :type 'String' is not a subtype of type 'int'
copied to clipboard
If a type is not indicated, Dart infers the type from the current assignment and [11,12,13] will be a List<int>.
From there, you can only add other <int> without errors, nothing else like "hello" without a crash.
Similar things can happen with a map.
This code will fail :
root.results = {"elapsed_time": 30, "temperature": 18} // is ok but result is a List of Map<String, int>
root.results.time = "12:58:00"; // will fail : type 'String' ("12:58:00") is not a subtype of type 'int' of 'value'
copied to clipboard
add <dynamic>
Think about adding type
root.data = <dynamic> [11, 12, 13];
root.data.add(14); // ok
root.data.add("hello"); // ok
copied to clipboard
root.results = <String,dynamic>{"elapsed_time": 60, "temperature": 40};
root.results.time = "12:58:00"; // now ok !
copied to clipboard
If you use constructors with a String structure, or 'dart json', MapList do the job of enlarging types to dynamic.
Logged errors #
MapList try to avoid runtime errors:
If something is wrong, it logs Warning but continue without errors:
On a wrong get, it logs message and returns null .
On a wrong set : it logs message and do nothing else .
(To see the messages, you must set a logging listener in your code (@see standard logging package).)
common warning : using List as Map or Map as List
aList["toto"]="riri";
**WARNING** index ["toto"] must be applied to a map. no action done.
copied to clipboard
aMap[0]="lulu":
**WARNING** [0] must be applied to a List. no action done.
copied to clipboard
print(aList.price);
**WARNING** Naming error: trying to get a key "price" in a List. null returned
copied to clipboard
Wrong index in List
print(root[200]);
**WARNING**: unknown accessor: . [200] : null returned .(followed by original dart error 'Not in range..')
copied to clipboard
Wrong json data in a String at runtime (if direct inline code, compiler will warn )
dynamic root = MapList('{"this": is not a valid entry }');
**WARNING** wrong json data. null returned .
(followed by original conversion error)
copied to clipboard
remaining runtime that can throw errors
Mainly Type mismatch if inline data are not correctly casted .
Forgotten nullable in the evaluated path.
Leaving dart inline tolerance in script or json : comma at the end [11,12,]
some words about Yaml
I do prefer coding in yaml rather in json, but this have some defaults :
var yamlStructure = loadYaml(yamlString);
dynamic root = MapList(yamlStructure);
print(root.show.name); // ok
root.show.name = "new video title";
-> 'runtime Error: Unsupported operation: Cannot modify an unmodifiable Map';
copied to clipboard
If all get can work, no set are allowed because the standards yamlMap and yamlList are read only.
tips for Yaml
The most simple way to transform a read-only yaml in a full compliant json is the following :
root = MapList(json.decode(json.encode(yamlStructure)));
copied to clipboard
Some More details #
A MapList has a .json accessor to get the wrapped one if necessary.
MapList works with pointers, not copies :
json data stay synchronised between :
direct access to json
use with MapList in code
or use of MapList interpreter.
Embed MapList in a class with specific methods #
As is, MapListMap and MapListList are in the category of DataObjects
You can mix free dynamic set of data and classical methods within a class that extends a MapList flavour.
Such a class is in an example with the following class Person :
class Person extends MapListMap{
Person(some):super(some);
}
copied to clipboard
using dot notation inside the class : keyword me. #
As properties are free and in a json , a method cannot use this.someProperty as someProperty is not declared in class.
To allow retrieval of properties with dot notation, just use the keyword me. ( which is a cast of this as dynamic ).
In examples, we define an internal getter to the class Person, using dynamic data with me. :
int get age {
return (DateTime.now().year - me.birthDate.year);
}
copied to clipboard
MapList access interpreter #
An interpreter have some well known use cases :
applying create or update on a data set from textual messages
free interaction with data not known at compile time (knowledge base, blackboard pattern,...)
using maps and lists as an open graph
underlying base : JsonNode #
You don't really need to use JsonNode as such, but MapList uses it to walk the graph.
JsonNode is a kind of canoe that navigates on the data graph with :
fromNode
edge
toNode
(ascript : the path or the remaining path)
When you create a JsonNode with a path, it returns the last step of its journey.
To view itn the toString returns (type) fromNode -- last edge in use---> (type) toNode
(As a node could be a large thing, toString returns the beginning... of the data )
returning a data leaf
Below is shown the internal structure to see internal data.
var aJson = [ [1, 2, 3], [11, 12], {"A": "AA", "B": "BB"}, "andSoOn" ];
print(JsonNode(aJson, '[0][1]')); // (list)[1, 2, 3] --- 1 -> (int) 2
print(JsonNode(aJson, '[2]["B"]')); // (map){A: AA, B: BB} --- B -> (String) BB
print(JsonNode(aJson, '[2].length')); // (map){A: AA, B: BB} --- length -> (int) 2
copied to clipboard
returning a tree branch
var aJson = [ [1, 2, 3], [11, 12], {"A": "AA", "B": "BB"}, "andSoOn" ];
print(JsonNode(aJson,'[0]')); // (list)[[1, 2, 3], [11... --- 0 -> (list) [1, 2, 3]
print(JsonNode(aJson,'[2]')); //(list)[[1, 2, 3], [11... --- 2 -> (map) {A: AA, B: BB}
copied to clipboard
If you plan to use directly JsonNode, you can get the data by .value
(which is a convenient name to get the last toNode )
assert(JsonNode(aJson, '[2].B').value == "BB");
special words
JsonNode recognize some keywords:
.length
.isEmpty
.isNotEmpty
.last (on Lists )
.clear()
Note about length
Always use .length to get the length of a List or a Map. If there is a key length in a map, you can reach it with notation ["length"]
dynamic store = MapList('{"bikes":[{"name":"Fonlupt", "length":2.1, "color" : "green" }]}');
assert(store.eval('bikes[0].length') == 3);
assert(store.eval('bikes[0]["length"]')== 2.1);
copied to clipboard
how a caller can create unknown new data
When a path ends with an unknown name in a map, the last node is null but not the trip :
print(JsonNode(aJson, 'questions.newData'));// (map){A: AA, B: BB} --- newData -> (Null) null
copied to clipboard
A caller, like MapList do, can check the results and set the data with fromNode[edge] .
(if the path starts at the very beginning, the fromNode is also null and the edge must be applied to the root )
Same Hell of types in interpreter
Same precautions has to be taken on datatypes to avoid bad surprises.
Writing inline data with types could be cumbersome :
var aJson = <dynamic>[ [1, 2, 3], <String,dynamic> {"A": "AA", "B": "BB"}, "andSoOn" ];
copied to clipboard
Tip: Prefer using a json String and let MapList do a json.decode(string) do the job.
type in your inline structure to see its correctness : [ [1, 2, 3], {"A": "AA", "B": "BB"}, "andSoOn" ]
wrap it in quotes : '[ [1, 2, 3], {"A": "AA", "B": "BB"}, "andSoOn" ]'
use it in MapList(someString)
MapList eval(script) method : get and set data by script #
MapList uses underline the previous JsonNode mechanism, get the value and allows to create and set data.
get data
You can use same notations than in code to access a data, classical or dot notation.
MapList will return directly the data:
for an end leaf, it returns the value,
for an intermediary node, it returns the json wrapped in a new MapList (returning a MapList allows to combine interpreter and direct code with dot notation in code.)
Notice that the root is the executor and is not repeated in the path :
The script below returns a simple data :
dynamic book = MapList('{ "name":"zaza", "friends": [{"name": "lulu" }]}');
print(book.eval('friends[0].name')); // --> lulu);
copied to clipboard
All notation styles are allowed :
root.eval('["show"]["videos"][1]["questions"][1]["name"]') // classical notation
root.eval('show.videos[1].questions[1].options[2].answer') // dot notation
Special word returns also direct values:
if (store.eval('store.bikes.isEmpty')) print ('what a disaster');
copied to clipboard
scripts to set data #
assignment with equal symbol
MapList interpreter takes care of an assignment in the script.
The Left Hand Side of a script with assignment is the path to get by MapList.
The Right Hand Side is evaluated as a simple type data or as a json structure.
dynamic squad = MapList(); // will create a default Map
squad.eval('name = "Super hero squad"'); // add a String data
squad.eval('homeTown = "Metro City"'); // another
squad.eval('formed = 2016'); // add an int
squad.eval('active = true'); // add a bool
squad.eval('score = 38.5'); // add a double
squad.eval('overhauls = ["2008/04/10", "2102/05/01", "2016/04/17"]'); // add structured data
copied to clipboard
special functions to create or update data
add(something)
Only for Lists : add a new element
addAll(several something)
add all elements of a map to a map
all elements of a list to a list.
remove(something)
Remove an entry of a map
Remove an element in a list
length = <int>
force the length of a List
errors and logs #
See the previous chapter on errors and logs for common access errors.
Some errors normally detected by compiler can happen in interpreted string.
As an example below is a missing parenthesis around functions.
root.eval('clear'); // WARNING cannot search a key (clear) in a List<dynamic>
root.eval('clear()'); // ok
copied to clipboard
Nullable capacities #
Interpreter takes care of nullable notations at all levels :
store.eval('wrongName?.someData')
store.eval("book[4]?.author
store.eval("bookList?[0]") // Even if Dart is not in 9.2, the interpreter allows this nullable.
store.eval("bookList?[0]?.date")
copied to clipboard
Weakness
Probably some in the analysis of syntax in interpreter : in case of trouble verify deeply your strings.
MapList uses Symbol without mirrors and get the symbol name by hand : This could have issues with dart.js minifier, this has not been tested here.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.