I am calling an Api that returns data in XMl
I then convert it from XML to Json using Xml2Json, to then decode and acheive a JsonMap, which is returning a map well.
When I then go to do locations.fromJson to be able to call data from my model is is returning as null.
I guess converting from XML may complicate but I have tried all possibilities, parsing the entire response, the section I need and modifying the model in all the ways I could.
The data is returning fine as Json, but there is just some disconnect when parsing it with my model, made via quicktype.io
When I call it in any way, be it print or a data retrieval, it returns on null at vehicleActivity
The call
Future<Locations> fetchLiveLocations() async {
var client = http.Client();
var locations;
Xml2Json xml2Json = new Xml2Json();
try{
var response = await client.get(
'https_call');
if (response.statusCode == 200) {
xml2Json.parse(response.body);
var jsonString = xml2Json.toGData();
var jsonMap = json.decode(jsonString);
//jsonMap is returning fine
locations = Locations.fromJson(jsonMap);
//Returning as null
}
} catch(Exception) {
return locations;
}
return locations;
}
Top part of Json Model
import 'dart:convert';
Locations locationsFromJson(String str) => Locations.fromJson(json.decode(str));
String locationsToJson(Locations data) => json.encode(data.toJson());
class Locations {
Locations({
this.vehicleActivity,
});
List<VehicleActivity> vehicleActivity;
factory Locations.fromJson(Map<String, dynamic> json) => Locations(
vehicleActivity: List<VehicleActivity>.from(json["VehicleActivity"].map((x) => VehicleActivity.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"VehicleActivity": List<dynamic>.from(vehicleActivity.map((x) => x.toJson())),
};
}
class VehicleActivity {
VehicleActivity({
this.recordedAtTime,
this.itemIdentifier,
this.validUntilTime,
this.monitoredVehicleJourney,
this.extensions,
});
DateTime recordedAtTime;
String itemIdentifier;
DateTime validUntilTime;
MonitoredVehicleJourney monitoredVehicleJourney;
Extensions extensions;
factory VehicleActivity.fromJson(Map<String, dynamic> json) => VehicleActivity(
recordedAtTime: DateTime.parse(json["RecordedAtTime"]),
itemIdentifier: json["ItemIdentifier"],
validUntilTime: DateTime.parse(json["ValidUntilTime"]),
monitoredVehicleJourney: MonitoredVehicleJourney.fromJson(json["MonitoredVehicleJourney"]),
extensions: Extensions.fromJson(json["Extensions"]),
);
XML File Returned
<Siri xmlns="http://www.siri.org.uk/siri" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.siri.org.uk/siri http://www.siri.org.uk/schema/2.0/xsd/siri.xsd" version="2.0">
<ServiceDelivery>
<ResponseTimestamp>2021-12-03T18:11:05.408806+00:00</ResponseTimestamp>
<ProducerRef>ItoWorld</ProducerRef>
<VehicleMonitoringDelivery>
<ResponseTimestamp>2021-12-03T18:11:05.408806+00:00</ResponseTimestamp>
<RequestMessageRef>5747b24f</RequestMessageRef>
<ValidUntil>2021-12-03T18:16:05.408806+00:00</ValidUntil>
<ShortestPossibleCycle>PT5S</ShortestPossibleCycle>
<VehicleActivity>
<RecordedAtTime>2021-12-03T18:10:01+00:00</RecordedAtTime>
<ItemIdentifier>ad2c7031-ceac-4e7c-bc0c-9e667ad00dfe</ItemIdentifier>
<ValidUntilTime>2021-12-03T18:16:05.408968</ValidUntilTime>
<MonitoredVehicleJourney>
<LineRef>4</LineRef>
<DirectionRef>inbound</DirectionRef>
<FramedVehicleJourneyRef>
<DataFrameRef>2021-12-03</DataFrameRef>
<DatedVehicleJourneyRef>4_20211203_18_04</DatedVehicleJourneyRef>
</FramedVehicleJourneyRef>
<PublishedLineName>4</PublishedLineName>
<OperatorRef>FTVA</OperatorRef>
<DestinationRef>03700324</DestinationRef>
<VehicleLocation>
<Longitude>-0.719601</Longitude>
<Latitude>51.520305</Latitude>
</VehicleLocation>
<Bearing>30.0</Bearing>
<BlockRef>801312</BlockRef>
<VehicleRef>69921</VehicleRef>
</MonitoredVehicleJourney>
<Extensions>
<VehicleJourney>
<Operational>
<TicketMachine>
<TicketMachineServiceCode>B4</TicketMachineServiceCode>
<JourneyCode>1815</JourneyCode>
</TicketMachine>
</Operational>
<VehicleUniqueId>69921</VehicleUniqueId>
<DriverRef>801312</DriverRef>
</VehicleJourney>
</Extensions>
</VehicleActivity>
Here's an example VehicleActivity class - note that it doesn't handle any errors like missing XML tags or unparsable dates, which you should add yourself.
class VehicleActivity {
VehicleActivity({
this.recordedAtTime,
this.itemIdentifier,
this.validUntilTime,
});
DateTime? recordedAtTime;
String? itemIdentifier;
DateTime? validUntilTime;
factory VehicleActivity.fromElement(XmlElement vaElement) => VehicleActivity(
recordedAtTime: DateTime.parse(
vaElement.findElements('RecordedAtTime').first.text,
),
itemIdentifier: vaElement.findElements('ItemIdentifier').first.text,
validUntilTime: DateTime.parse(
vaElement.findElements('ValidUntilTime').first.text,
),
);
}
You would use the factory method from the enclosing tag (similar to how your JSON parsers are written), for each of the vehicle activity tags it finds. (Note that XML can have multiple identically named tags, which is why the code is using first to find the (hopefully) one and only tag. If you want to parse XML correctly you need to deal with this - and note that the trip through JSON could break this - but not with your simple schema.)
Here's a simple example that just finds all the vehicle activity tags:
final doc = XmlDocument.parse(utf8.decode(response.bodyBytes));
final allActivities = doc
.findAllElements('VehicleActivity')
.map((e) => VehicleActivity.fromElement(e))
.toList();
print(allActivities);
Related
I keep getting this error:
NoSuchMethodError (NoSuchMethodError: The method 'map' was called on null.
Receiver: null
Tried calling: map(Closure: (dynamic) => Name1Name2))
I am trying very hard to solve this error. I am certain I am not mapping the data properly.
My fake REST API from which I'm fetching and sending the data to
"messagesBetweenTwoUsers": {
"Name1Name2": [
{
"senderName": "John",
"message": "How are you doing?"
}
]
}
This is my get messages file
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'messages_model.dart';
Future<List<MessageModel>> getMessages() async {
try {
var getResponse = await http.get(
Uri.parse("http://127.0.0.1:3000/messagesBetweenTwoUsers"),
);
if (getResponse.statusCode == 200) {
String getData = getResponse.body;
var jsonData =
jsonDecode(getData);
var getResult = jsonData["Name1Name2"].map(
(e) => Name1Name2.fromJson(e),
);
//here it throws me an error, in getResult, right when I am using map()
//NoSuchMethodError (NoSuchMethodError: The method 'map' was called on null.
//Receiver: null
//Tried calling: map(Closure: (dynamic) => Name1Name2))
return getResult;
} else {
return [];
}
} catch (error) {
debugPrint("ERROR IN FETCHING FROM GET-MESSAGE-API: $error");
}
return [];
}
My model class
import 'package:meta/meta.dart';
import 'dart:convert';
class MessageModel {
MessageModel({
required this.name1Name2,
});
final List<Name1Name2> name1Name2;
factory MessageModel.fromRawJson(String str) => MessageModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory MessageModel.fromJson(Map<String, dynamic> json) => MessageModel(
name1Name2: List<Name1Name2>.from(json["name1name2"].map((x) => Name1Name2.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"name1name2": List<dynamic>.from(name1Name2.map((x) => x.toJson())),
};
}
class Name1Name2 {
Name1Name2({
required this.senderName,
required this.message,
});
final String senderName;
final String message;
factory Name1Name2.fromRawJson(String str) => Name1Name2.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Name1Name2.fromJson(Map<String, dynamic> json) => Name1Name2(
senderName: json["senderName"],
message: json["message"],
);
Map<String, dynamic> toJson() => {
"senderName": senderName,
"message": message,
};
}
You need to learn null safety but let me tell you how to fix your current code. It is simple!
Put a question mark ? before calling .map(). You should do that everywhere. For example, this should be
jsonData["Name1Name2"].map( ......
like this
jsonData["Name1Name2"]?.map( ......
The question mark ? simply check if the value before it is null and return null if it is or process the rest if it isn't.
try it like this
if (getResponse.statusCode == 200) {
String getData = getResponse.body;
var jsonData =
await json.decode(getData);
var getResult = jsonData["Name1Name2"].map(
(e) => Name1Name2.fromJson(e),
);
return getResult;
} else {
return [];
}
dont forget to import 'dart:convert';
This error says you are accessing a key which is not found directly into the JSON data. Looking into your JSON data, you have the key Name1Name2 nested into the messagesBetweenTwoUsers key value. Therefore, in order to access the value of the Name1Name2 key you should call first the messagesBetweenTwoUsers key, like this:
var getResult = jsonData["messagesBetweenTwoUsers"]["Name1Name2"].map(
(e) => Name1Name2.fromJson(e),
);
EDITED:
Use this JSON model instead:
{
"participants": [
{
"senderName": "John",
"receiverName": "Jakiro",
"message": "How are you doing?"
}
]
}
And decode the JSON data like this:
var getResult = (jsonData['participants'] as List).map(
(e) => Name1Name2.fromJson(e),
).toList();
As you are looking/decoding the json is not right, so the following website will help you do it - quicktype. I highly encourage you to explore the website with different options.
As #Mayo Win said. You do need to learn Null Safety feature. Otherwise it make your code prone to error.
From your method getMessages() just call the following -
CustomDataModel customDataModel = customDataModelFromJson(response.bodyString ?? '');
I have the following JSON scalar:
"""
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
scalar JSON
which I am trying to convert since my query is accepting input: JSON. When testing using graphql playground, query is JSON object thus the following works:
query {
carts(where: {
owner:{id: "xxx"}
store:{name: "yyy"}
}) {
id
}
}
# query is the starting from the where: {...}
# build.yaml
# build.yaml
gql_build|schema_builder: #same for gql_build|schema_builder + gql_build|var_builder + ferry_generator|req_builder:
options:
type_overrides:
DateTime:
name: DateTime
JSON:
name: BuiltMap<String, dynamic>
import: 'package:built_collection/built_collection.dart'
gql_build|serializer_builder:
enabled: true
options:
schema: myapp|lib/graphql/schema.graphql
custom_serializers:
- import: 'package:myapp/app/utils/builtmapjson_serializer.dart'
name: BuiltMapJsonSerializer
This is the custom serializer (builtmapjson_serializer.dart)
//// lib/app/utils/builtmapjson_serializer.dart
import 'package:built_collection/built_collection.dart';
import "package:gql_code_builder/src/serializers/json_serializer.dart";
class BuiltMapJsonSerializer extends JsonSerializer<BuiltMap<String, dynamic>> {
#override
BuiltMap<String, dynamic> fromJson(Map<String, dynamic> json) {
print('MyJsonSerializer fromJson: $json');
return BuiltMap.of(json);
}
#override
Map<String, dynamic> toJson(BuiltMap<String, dynamic> operation) {
print('MyJsonSerializer toJson: ${operation.toString()}');
return operation.asMap();
}
}
and the usage:
Future testQuery() async {
Map<String, dynamic> queryMap = {
"where": {
"owner": {
"id": "xxx",
"store": {"name": "yyy"}
}
}
};
final req = GFindCartsReq((b) {
return b..vars.query.addAll(queryMap);
});
var resStream = _graphQLService.client.request(req);
var res = await resStream.first;
print(
'linkExceptions: ${res.linkException}'); // Map: LinkException(Bad state: No serializer for '_InternalLinkedHashMap<String, Map<String, Object>>'.)
}
So whenever I try to query, it is throwing the linkException stated in the comment on the last line of usage. Any idea what should be the way of serializing it?
// Write query like this
query FindCarts($owner_id: String!, $store_name: String!) {
carts(where: {
owner:{id: $owner_id}
store:{name: $store_name}
}) {
id
}
}
// And make request like this:
final req = GFindCartsReq((b) => b..vars.store_name = 'XXX'..vars.owner_id = 'YYY');
I think you may be misunderstanding the use case. they are there to serialize and deserialize the response if you want to end up with a Dart object that's different from graphql representation. you may want to try rereading this section:
https://ferrygraphql.com/docs/custom-scalars/#create-a-custom-serializer
in the example in the docs, the graphql schema returns an int for the timestamp, but we want to actually use a Date object, so that's the purpose of the serializer. it tells ferry to deserialize the int in our responses to a Date so we can use a Date in our dart code. you could still use a json serializer (like in the examples you linked to) but it still would not be in the way you're trying to use it -- it would be if your schema returns a json string and you want to deserialize the json string. for example, in my case, my graphql schema actually does return a "jsonb" type on some objects. in order to handle that, i'm using built_value's default json_object like this:
(
...
type_overrides:
jsonb:
name: JsonObject
import: "package:built_value/json_object.dart"
custom_serializers:
- import: "package:built_value/src/json_object_serializer.dart"
name: JsonObjectSerializer
I want to send a List of ManageTagModel in a multipart request along with other models and files..
I am not certain of how to send this List of model..
This is my code for sending the multipart request without the List:
var uri = Uri.parse(...);
final request = http.MultipartRequest('Post', uri);
request.fields['Id'] = '3';
request.fields['Name'] = siteModel.name;
request.fields['MapAddress'] = siteModel.mapAddress;
request.fields['Country'] = siteModel.country;
request.fields['City'] = siteModel.city;
request.fields['CategoryNb'] = siteModel.categoryNb;
request.fields['UserId'] = userId;
request.fields['Caption'] = caption;
for (File i in
multipleFiles) {
final mimeTypeData =
lookupMimeType(i.path, headerBytes: [0xFF, 0xD8]).split('/');
print("IMAGE: " + i.path);
// Attach the file in the request
final file = await http.MultipartFile.fromPath('files', i.path);
print(mimeTypeData[0] + " mimeTypeData[0]");
print(mimeTypeData[1] + " mimeTypeData[1]");
request.files.add(file);
this is my model:
import 'dart:convert';
class ManageTagModel {
String posX;
String posY;
String postOrder;
String tagger;
String tagged;
ManageTagModel(
{this.posX, this.posY, this.postOrder, this.tagger, this.tagged});
//Flutter way of creating a constructor
factory ManageTagModel.fromJson(Map<String, dynamic> json) => ManageTagModel(
posX: json['PosX'],
posY: json['PosY'],
postOrder: json['PostOrder'],
tagged: json['Tagged'],
tagger: json['Tagger']);
Map<String, dynamic> toMap() {
return {
"PosX": posX,
"PosY": posY,
"PostOrder": postOrder,
"Tagger": tagger,
"Tagged": tagged
};
}
}
List<ManageTagModel> fromJson(String jsonData) {
// Decode json to extract a map
final data = json.decode(jsonData);
return List<ManageTagModel>.from(
data.map((item) => ManageTagModel.fromJson(item)));
}
String toJson(ManageTagModel data) {
// First we convert the object to a map
final jsonData = data.toMap();
// Then we encode the map as a JSON string
return json.encode(jsonData);
}
List encodeToJson(List<ManageTagModel> list) {
List jsonList = List();
list.map((item) => jsonList.add(item.toMap())).toList();
return jsonList;
}
My backend c# method has a parameter List
Any help is appreciated!!
I'm pretty sure I'm quite late here and you might have already found a solution. I have gone through multiple threads and didn't actually find any answers but discovered myself out of frustration and thought to myself that the answer actually is still not out there for any other lost human soul. So here is my solution for anyone still stuck here which is quite intuitive.
You simply have to add all the elements of the list to the request as "files" instead of "fields". But instead of fromPath() method, you have to use fromString().
final request = http.MultipartRequest('Post', uri);
List<String> ManageTagModel = ['xx', 'yy', 'zz'];
for (String item in ManageTagModel) {
request.files.add(http.MultipartFile.fromString('manage_tag_model', item));
}
This worked out for me and I hope it works for you too.
if the data was not string
for (int item in _userData['roles']) {
request.files
.add(http.MultipartFile.fromString('roles', item.toString()));
}
i have class that generate json list this is my class
class Tool{
String Name;
bool selection;
String englishName;
Map<String, dynamic> toJson() {
return {
'Name': persianName,
'selection': selection,
'englishName': englishName
};
}
}
List<Tool> tools=new List();
setTool(tool){
tools.add(tool);
}
toolsLength(){
return tools.length;
}
updatetool(index,goal){
tools[index]=goal;
}
getTool(index){
return tools[index];
}
getAllTools(){
return tools;
}
and this is dio library that send my list to server every thing is ok but my array is into Double quotation in other word my list is string how to pick up double quotation around of json array . if assume my json array hase
"tools": [{"name":"jack","selection" : false " ,"englishName":"jock"}]
result is :
"tools": "[{"name":"jack","selection" : false " ,"englishName":"jock"}]"
how fix it this is my class for send
FormData formData = new FormData.from({
"tools":jsonEncode(getAllTools().map((e) => e.toJson()).toList()) ,
});
response = await
dio.post("${strings.baseurl}/analyze/$username/$teacher", data:
formData);
print("----------> response is :"+response.toString());
Edit
You can paste the following code to DarPad https://dartpad.dartlang.org/
The following demo shows transform your Json String to Tool List and Convert your Tool List to JSON string again and use map.toJson
var yourresult = toolList.map((e) => e.toJson()).toList();
you can see result in picture
use FormData.from need to know what your api's correct JSON String.
Please test your web api with Postman, if you success, you will know correct format string.
In your code
FormData formData = new FormData.from({
"tools":jsonEncode(getAllTools().map((e) => e.toJson()).toList()) ,
});
if you are trying to to do
FormData formData = new FormData.from({
"tools":'[{"name":"jack","selection" : false ,"englishName":"jock"}, {"name":"jack2","selection" : false ,"englishName":"jock2"}]' ,
});
so you can get your json string first and put it in FormData
var toolsJson = toolsToJson(toolList);
FormData formData = new FormData.from({
"tools":toolsJson ,
});
full demo code, you can paste to DartPad to see string and list conversion
import 'dart:async';
import 'dart:io';
import 'dart:core';
import 'dart:convert';
class Tools {
String name;
bool selection;
String englishName;
Tools({
this.name,
this.selection,
this.englishName,
});
factory Tools.fromJson(Map<String, dynamic> json) => new Tools(
name: json["name"],
selection: json["selection"],
englishName: json["englishName"],
);
Map<String, dynamic> toJson() => {
"name": name,
"selection": selection,
"englishName": englishName,
};
}
main() {
List<Tools> toolsFromJson(String str) => new List<Tools>.from(json.decode(str).map((x) => Tools.fromJson(x)));
String toolsToJson(List<Tools> data) => json.encode(new List<dynamic>.from(data.map((x) => x.toJson())));
var toolsStr = '[{"name":"jack","selection" : false ,"englishName":"jock"}, {"name":"jack2","selection" : false ,"englishName":"jock2"}]';
var toolList = toolsFromJson(toolsStr);
var toolsJson = toolsToJson(toolList);
print("toolsJson ${toolsJson} \n");
var toolsmap = toolList[0].toJson();
print("toolsmap ${toolsmap.toString()}\n");
var yourresult = toolList.map((e) => e.toJson()).toList();
print(yourresult.toString());
}
you can paste your JSON string to https://app.quicktype.io/, you will get correct Dart class
correct JSON string of your sample. in false keyword followed by a " cause JSON string parsing error.
[
{"name":"jack",
"selection" : false ,
"englishName":"jock"
}
]
code to parse JSON string and encode to List, use toJson will convert to JSON string you need to send with dio form
// To parse this JSON data, do
//
// final tools = toolsFromJson(jsonString);
import 'dart:convert';
List<Tools> toolsFromJson(String str) => new List<Tools>.from(json.decode(str).map((x) => Tools.fromJson(x)));
String toolsToJson(List<Tools> data) => json.encode(new List<dynamic>.from(data.map((x) => x.toJson())));
class Tools {
String name;
bool selection;
String englishName;
Tools({
this.name,
this.selection,
this.englishName,
});
factory Tools.fromJson(Map<String, dynamic> json) => new Tools(
name: json["name"],
selection: json["selection"],
englishName: json["englishName"],
);
Map<String, dynamic> toJson() => {
"name": name,
"selection": selection,
"englishName": englishName,
};
}
how to update JSON value. I am using flutter with a REST API to change the data but I'm struggling how to refresh my JSON data sorry friends I don't know much about the flutter so please help me out about it please find JSON'S Object below:
{
id: 1,
clef: "Y8F5eEo0",
IndiceSensibilite: "0.04",
Objectif: "1.00"
}
I want to update the value of IndiceSensibilite using a textField.
I m here for more clarification.
i will be very thankful if there's someone who gonna help me.
You can transform the JSON into an Object, make your modifications and back to JSON:
import 'dart:convert'; // make sure you imported this library
String jsonValue = '{"id": 1, "clef": "Y8F5eEo0", "indiceSensibilite": "0.04", "objectif": "1.00" }'; // original JSON
Use a Future to convert it into Object:
Future<ToObject> jsonToObject() async {
var parse = json.decode(jsonValue);
return ToObject.parseJson(parse);
}
and your Object (ToObject in my case) will have the following model and methods:
class ToObject {
int id;
String clef;
String indiceSensibilite;
String objectif;
ToObject({this.id, this.clef, this.indiceSensibilite, this.objectif});
factory ToObject.parseJson(Map<String, dynamic> json) => ToObject(
id: json['id'], clef: json['clef'], indiceSensibilite: json['indiceSensibilite'], objectif: json['objectif']);
Map<String, dynamic> toJson() =>
{'id': id, 'clef': clef, 'indiceSensibilite': indiceSensibilite, 'objectif': objectif};
}
I used a FutureBuilder to get the data, modify the value and back to JSON:
FutureBuilder<ToObject>(
future: jsonToObject(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
var object = snapshot.data;
object.indiceSensibilite = "43.00";
return Text(json.encode(object.toJson()));
} else {
return Text("Loading...");
}
},
)
Your end result is a modified JSON to use as you wish: