toJson() invalid when recursively encode tree data structure - dart

I have a Dart class that I am using as a node class for a tree data structure.
My goal here is to encode objects of this class and its child nodes recursively.
I have a toJson() method that takes the child Nodes List and calls jsonencode on them.
class Node{
String name;
Map<String, String> attributes;
List<Node> children = List<Node>();
Node(this.name, attributes) {
this.attributes = attributes;
this.children = List<Node>();
}
Node.fromJson(Map<dynamic,dynamic> _map) {
this.name = _map['name'];
this.children = new List<Node>();
this.attributes = _map['attributes'][0];
for(var i = 0; i < _map['children'].length;i++){
Node temp = new Node.fromJson(_map['children'][i]);
this.addChild(temp);
}
}
Map<String, dynamic> toJson() => {
'name': name,
'attributes': [attributes],
'children': [
...this.children.map(jsonEncode)
]
};
}
I have a unit test i created to test this functionality:
Node nodeMap = {
"name": "Name",
"attributes": [
{"#htag1": "tagval1"}
],
"children": [
{
"name": "NameChild1",
"attributes": [
{"#htag2": "tagval2"}
],
"children": []
},
{
"name": "NameChild2",
"attributes": [
{"#htag3": "tagval3"}
],
"children": []
}
]
};
UNode unodeInst = new UNode.fromJson(nodeMap);
// Act
var nodeCreate = nodeInst.toJson();
// Assert
expect(nodeCreate, equals(nodeMap));
Here is the output of my unit test
Expected: {
'name': 'Name',
'attributes': [{'#htag1': 'tagval1'}],
'children': [
{
'name': 'NameChild1',
'attributes': [{'#htag2': 'tagval2'}],
'children': []
},
{
'name': 'NameChild2',
'attributes': [{'#htag3': 'tagval3'}],
'children': []
}
]
}
Actual: {
'name': 'Name',
'attributes': [{'#htag1': 'tagval1'}],
'children': [
'{"name":"NameChild1","attributes":[{"#htag2":"tagval2"}],"children":[]}',
'{"name":"NameChild2","attributes":[{"#htag3":"tagval3"}],"children":[]}'
]
}
Which: at location ['children'][0] is '{"name":"NameChild1","attributes":[{"#htag2":"tagval2"}],"children":[]}' which expected a map
As you see its not encoding my object correctly.
I believe this is happening because when i reclusively call jsonencode this method returns a string that is placed into the children array.
I believe part of my problem is that i dont fully understand the d diffrence between jsonencode() and toJson().
It is my understanding that jsonencode() calls toJson().. but jsonencode() returns a string and toJson() returns a Map<String, dynamic>.. so i think what i want here is to call toJson() recursively and not jsonencode.
Does this sound correct?
But i cannot figure out how to do this on a list in this situation.
I have tried the following
...this.children.map(this.toJson())
but i get "The argument type 'Map<String, dynamic>' can't be assigned to the parameter type 'dynamic Function(Node)'"
...this.children.forEach((element) {element.toJson()})
but i get "Spread elements in list or set literals must implement 'Iterable'"
Does this mean i have to implement the Iterable interface in my class?

You're just using the map method incorrectly. Use the following instead.
[
...this.children.map((e) => e.toJson())
]
It's also unnecessary to use spread with a literal list or use this. You can simplify the code to just
children.map((e) => e.toJson()).toList()

Related

How to deserialize a json object into rust understandable code using enums?

I need to deserialize ( and later on serialize ) a piece of data that has this type of a structure :
{
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"Record"
]
},
"arg": {
"type": "RowCons",
"val": {
"label": "953e3dd6-826e-1985-cb99-fd4ed4da754e",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "nullam-senectus-port - Text",
"isBindable": true,
"defaultValue": [
{
"id": "4a05486f-f24d-45f8-ae13-ab05f824d74d",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Nullam senectus porttitor in eget. Eget rutrum leo interdum."
},
"children": [],
"text": true
}
],
"isUnlinked": false,
"isDefault": false
}
},
"tail": {
"type": "RowCons",
"val": {
"label": "94f603df-d585-b45a-4252-9ec77cf5b13c",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "best-services - Text",
"isBindable": true,
"defaultValue": [
{
"id": "6265ca45-3f69-4844-97e2-c05bbfb9fee5",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Best Services"
},
"children": [],
"text": true
}
]
}
},
"tail": {
"type": "RowEmpty"
}
}
}
}
}
}
}
I do not know what this data exactly is, but I know this is trying to represent a function/element that takes in values and defaults for those values as parameters/properties.
I want to deserialize it using serde and consequently serialize it too.
I have so far been able to write something that sort of works but not really :
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type", content = "val")]
pub enum WebflowPropDataType {
TypeApplication {
con: Box<WebflowPropDataType>, // Normally Passes the Type Constructor
arg: Box<WebflowPropDataType>, // Normally Passes the Row Constructor
},
TypeConstructor(Vec<String>), // Stores Value of TypeConstructor
RowCons {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
row_con_type: Box<WebflowPropDataType>, // Stores the type of the Row
tail: Box<WebflowPropDataType>,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct WebflowRowConDataType {
#[serde(rename = "type")]
val_type: String, // TypeApplication
val: Box<WebflowPropDataType>,
}
This works for a structure like this :
{
"type": "TypeApplication",
"val":{
"con": {
"type": "TypeConstructor",
"val": []
},
"arg": {
"type": "RowEmpty"
}
}
}
but would fail if I try to work with initial example. I know this may be due to the lack of a proper arg type or maybe even the TypeApplication Enum hand is malformed.
I do see that a adjancent typing solution would be enough for most of the times but there are cases as seen in the example structure that there is a third value called system and I am unable to determine what type of approach would help me achieve this type of outcome.
How should I approach this problem in order to generate this type of code.
I am not asking anyone to write me a solution but to give me suggestion as to what my approach should be? Whether you'd know what type of data this is/how to generated this , or even if there are some other library I should look into to manipulate this type of data or maybe look at other approaches.
PS : - My end goal is to be able to generate / serialize this type of JSON code from already contained knowledge of properties and default values of a function/object.
Here are my recommendations:
Use just #[serde(tag = "type")] instead of #[serde(tag = "type", content = "val")]. You will have to handle val manually (extracting the current enum members into separate structs), but this allows you to also handle TypeApplication.system and Element.meta.
This also has the small benefit of reducing the amount of Boxes involved.
Consider whether all of the different cases in WebflowPropDataType can actually occur everywhere it's used. If not (maybe Element can only happen under TypeApplication.val.arg), then you may want to split the enum into multiple so that this is reflected in the type system.
Example for #1:
use serde::{Serialize, Deserialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationVal {
con: WebflowPropDataType, // Normally Passes the Type Constructor
arg: WebflowPropDataType, // Normally Passes the Row Constructor
}
// #[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationSystem {
label: String,
#[serde(rename = "isBindable")]
is_bindable: bool,
// TODO: defaultValue
#[serde(rename = "isUnlinked")]
#[serde(skip_serializing_if = "Option::is_none")]
is_unlinked: Option<bool>,
#[serde(rename = "isDefault")]
#[serde(skip_serializing_if = "Option::is_none")]
is_default: Option<bool>,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct RowConsVal {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
typ: WebflowPropDataType, // Stores the type of the Row
tail: WebflowPropDataType,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ElementMeta {
#[serde(rename = "multiLine")]
multi_line: bool,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum WebflowPropDataType {
TypeApplication {
val: Box<TypeApplicationVal>,
#[serde(skip_serializing_if = "Option::is_none")]
system: Option<TypeApplicationSystem>,
},
TypeConstructor {
val: Vec<String> // Stores Value of TypeConstructor
},
RowCons {
val: Box<RowConsVal>,
},
Element {
meta: ElementMeta,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}
playground

needs to convert Lists of map data into One map

My question is about dart....
the result I got
[{'sam': 8}, {'john': 822}]
I need to convert it into like below
{'sam': 8, 'john': 822}
pls help me.
thanks for reading
Something like this?
void main() {
final listOfMaps = [
{'sam': 8},
{'john': 822},
];
final map = {for (final map in listOfMaps) ...map};
print(map); // {sam: 8, john: 822}
}
Update after example of data has been uploaded
I have made the following example which parses the input you have posted as returns the expected data:
void main() {
final map = {
for (final map in someListOfMaps)
for (final voted in map['voted']! as List<Map<String, String>>)
for (final vote in voted.entries) vote.key: int.parse(vote.value)
};
print(map);
// {60e6956078fb6f42da: 1, 60e6956020d8bf42db: 5, 120d8bf42dffsww66: 1, jd58466daa4dw2: 20, gg4c577x6ad8ds6a: 6}
}
const someListOfMaps = [
{
"voted": [
{"60e6956078fb6f42da": "1"},
{"60e6956020d8bf42db": "5"}
],
"_id": "60e698fe78fb6120d8bf42dd",
"name": "donald"
},
{
"voted": [
{"120d8bf42dffsww66": "1"}
],
"_id": "60e698fe78fb6120d8bf42de",
"name": "barrack"
},
{
"voted": [
{"jd58466daa4dw2": "20"}
],
"_id": "60e698fe78fb6120d8bf42df",
"name": "malan"
},
{
"voted": [
{"gg4c577x6ad8ds6a": "6"}
],
"_id": "60e698fe78fb6120d8bf42e0",
"name": "kuma"
}
];
This is a longer approach and probably more understandable.
// the result I got [{'sam': 8}, {'john': 822}]
// I need to convert it into like below {'sam': 8, 'john': 822}
void main() {
final mapList = [{'sam': 8}, {'john': 822}];
print(mapListToJustMap(mapList)); // output: {sam: 8, john: 822}
// The <int> is not required
print(genericMapListToJustMap<int>(mapList)); // output: {sam: 8, john: 822}
}
Map<String, int> mapListToJustMap(List<Map<String, int>> mapList) {
// Create a new empty map object
final newMap = <String, int>{};
// Iterate through the mapList input
for (final singleMap in mapList) {
// add the current iteration to the new map object
newMap.addAll(singleMap);
}
return newMap;
}
// A generic approach
Map<String, T> genericMapListToJustMap<T>(List<Map<String, T>> mapList) {
// Create a new empty map object
final newMap = <String, T>{};
// Iterate through the mapList input
for (final singleMap in mapList) {
// add the current iteration to the new map object
newMap.addAll(singleMap);
}
return newMap;
}

Building a list of objects out of another list of lists parsed from a JSON file

I'm trying to build a page showing Open Street Map with routes. I've set up the OSM, and the routes/polylines should be added through a list of LatLng objects (an object consisting of two doubles marking the latitude and longitude of points connected by a line). What I want to do is fetch the user's location, and then get the latitudes and longitudes of the path along the route from the user's location to some other location, through the use of the Graphhopper API.
JSON returned from the API is as follows:
{
"hints":{
"visited_nodes.average":"40.0",
"visited_nodes.sum":"40"
},
"info":{
"copyrights":[
"GraphHopper",
"OpenStreetMap contributors"
],
"took":5
},
"paths":[
{
"distance":689.229,
"weight":408.670174,
"time":496240,
"transfers":0,
"points_encoded":false,
"bbox":[
15.23345,
44.103858,
15.238698,
44.105704
],
"points":{
"type":"LineString",
"coordinates":[
[
15.238079,
44.103858
],
[
15.238369,
44.104135
],
[
15.238698,
44.104337
],
[
15.238349,
44.104658
],
[
15.238155,
44.104889
],
[
15.237904,
44.105114
],
[
15.237713,
44.105236
],
[
15.237051,
44.105388
],
[
15.236858,
44.105457
],
[
15.236894,
44.105388
],
[
15.236866,
44.105314
],
[
15.236739,
44.105209
],
[
15.235663,
44.104713
],
[
15.234928,
44.105129
],
[
15.234886,
44.105037
],
[
15.234913,
44.10476
],
[
15.234786,
44.10476
],
[
15.234449,
44.105039
],
[
15.23355,
44.105704
],
[
15.23345,
44.105639
]
]
},
"legs":[
],
"details":{
},
"ascend":2.619999408721924,
"descend":3.4739990234375,
"snapped_waypoints":{
"type":"LineString",
"coordinates":[
[
15.238079,
44.103858
],
[
15.23345,
44.105639
]
]
}
}
]
}
Basically, I need to create a list of LatLng objects from this JSON ( an example: [LatLng(15.236866, 44.105314), LatLng(15.23355, 44.105704)] ), but, sadly, I have no clue how to do this. Any help, advice or guidance would be greatly appreciated.
I've tried searching through the web and hacking some code together, but I'm afraid that it didn't prove to be of much help.
Future<Points> _fetchCoordinates() async {
final response = await http.get(
'https://graphhopper.com/api/1/route?point=44.1035042,15.2385878&point=44.105091,15.2318734&vehicle=foot&locale=hr&key=<API_KEY>&points_encoded=false&instructions=false',
);
if (response.statusCode == 200) {
return Points.fromJson(json.decode(response.body));
} else {
throw Exception('Error');
}
}
class Points {
List<List<double>> coordinates;
Points({this.coordinates});
factory Points.fromJson(Map<String, dynamic> json) => Points(
coordinates: List<List<double>>.from(json["paths"]["points"]
["coordinates"]
.map((x) => List<double>.from(x.map((x) => x.toDouble())))));
Map<String, dynamic> toJson() => {
"coordinates": List<dynamic>.from(
coordinates.map((x) => List<dynamic>.from(x.map((x) => x))))
};
}
class Routing extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder<Points>(
future: _fetchCoordinates(),
builder: (context, snapshot) {
final List coordinates =
snapshot.hasData ? snapshot.data.coordinates : <LatLng>[];
if (snapshot.hasData) {
return MyCustomMap(
lat: 44.1035042,
lng: 15.2385878,
points: <List of LatLng objects, for example: [LatLng(41.234,
43.465)]>,
);
} else if (snapshot.hasError) {
return [...]
} else
return [...]
},
);
}
}
It appears that there's some error in my code; statement that's returned is located in the "else if (snapshot.hasError)" clause.
Define a LatLng class like this:
class LatLng {
double lat;
double lng;
LatLng(this.lat, this.lng);
}
then you can decode the json like this:
Map<String, dynamic> decoded = json.decode(j);
List<dynamic> co1 = decoded['paths'][0]['points']['coordinates'];
List<LatLng> coords = co1.map((pair) => LatLng(pair[0], pair[1])).toList();
You could still have a Points class that wrapped the List<LatLng>...

How to add sorting for field object of graphql type which refers to different graphql type?

I am using Neo4j dB and using pattern comprehension to return the values. I have 2 types Person and Friend:
(p:Person)-[:FRIEND_WITH]->(f:Friend)
Type Person{
id: String
name: String
friends: [Friend]
}
Type Friend{
id: String
name: String
}
type Query {
persons( limit:Int = 10): [Person]
friends( limit:Int = 10): [Friend]
}
What i want to do is to pull the array list of field friends (present in Person Type) in ascending order when the "persons" query executes. For e.g.
{
"data": {
"persons": {
"id": "1",
"name": "Timothy",
"friends": [
{
"id": "c3ef473",
"name": "Adam",
},
{
"id": "ef4e373",
"name": "Bryan",
},
(
"id": "e373ln45",
"name": "Craig",
},
How should I do it ? I researched regarding the sorting, but I did not find anything specific on the array object's sorting when we are using pattern comprehension in neo4j. Any suggestions would be really helpful !
I used the sortBy function of lodash to return the result into an ascending order.
And here is the graphql resolver query:
persons(_, params) {
let query = `MATCH (p:Person)
RETURN p{
.id,
.name,
friends: [(p)-[:FRIEND_WITH]->(f:Friend)) | f{.*}]
}
LIMIT $limit;`;
return dbSession().run(query, params)
.then(result => {
return result.records.map(record => {
let item = record.get("p");
item.friends = sortBy(item.friends, [function(i) {
return i.name;
}]);
return item;
})
})
}

Malformed object returned by query from a one-to-many Grails domain

Consider these two Grails domain classes:
class Agreement implements Serializable {
String code
String branchCode
...
static belongsTo = [agency: CollectingAgency]
static mapping = {
...
}
}
class CollectingAgency implements Serializable {
String type
String agencyClass
...
static hasMany = [agreement: Agreement]
static mapping = {
...
}
}
Now when I perform Agreement.findAll() it creates a sql similar to this (using loggingSql = true):
select agreement0_.agency_id as agency4_67_1_, agreement0_.AGREH_CD as
AGREH1_1_, agreement0_.AGREH_BRCHCD as AGREH2_1_,
...
agreement0_.agency_id as agency4_66_0_,
^^^^^^^^^
...
from RVAGREHDROTB agreement0_
where agreement0_.agency_id=?
The statement will not execute because of the unknown column(agency_id). And I wonder where do it gets the agency_id column? I haven't mapped any column with such name.
And when I try to query from the other side using CollectingAgency.findAll(), it returns a malformed Object:
[
{
type: "0400300",
agencyClass: "12",
...
agreement: [
Yes, like that, with the agreement key having an opening square bracket [ but no closing bracket ]. Plus, the other properties of the table are not retrieved.
How can I achieve a query with a resulting object similar to these:
Agreement.findAll():
[
{
code: "1212",
branchCode: "a014s",
...
agency: {
type: "0400300",
agencyClass: "12",
...
},
...
},
{
code: "1213",
branchCode: "a014z",
...
agency: {
type: "0400300",
agencyClass: "12",
...
},
...
},
...
]
and CollectingAgency.findAll():
[
{
type: "0400300",
agencyClass: "12",
...
agreement: [
{
code: "1212",
branchCode: "a014s",
...
},
{
code: "1213",
branchCode: "a014z",
...
},
...
]
},
...
]
In your Agreement class you have
static belongsTo = [agency: CollectingAgency]
which will create an 'agency' field in your class. the agency_id you see just maps your 'agency' field to the 'agency' column in the database.

Resources