An API is giving me a JSON response like so :
{
"amountCredited":0,
"isFirstOrder":false,
"orderItems":[
{
"_id":624342e1c66be9001d501230,
"status":2,
"pinCode":749326,
"kioskId":61bb3982089a66001db4ab77,
"kioskActivityId":620668ad433322b99557c874
}
]
}
I'm trying to access the data inside the "orderItems" in order to feed it to an existing parsing model in the App
order = OrderItemModel.fromJson(response.body['orderItems'] as Map<String, dynamic>);
but since the data inside orderItems JSON response is inside an array I can't access it this way..
How can I access it knowing that this JSON "orderItems" array will always only have one item as a response ?
Would something like response.body['orderItems' : [0]] enable me to access the first item data ?
Since you always know that the array will always have only one item. We'll first make it a List & then access the first element.
order = OrderItemModel.fromJson((response.body['orderItems'] as List<dynamic>).first as Map<String, dynamic>);
To understand it a bit better refer to the code below:
The JSON response contains an array, which we need to access.
final ordersArray = response.body['orderItems'] as List<dynamic>;
Then we want to access the first order (according to the question)
final firstOrder = ordersArray.first as Map<String, dynamic>;
Once we have the order, we'll convert it to the model
final order = OrderItemModel.fromJson(firstOrder);
EDIT:
as it was pointed out in a comment List objects have a getter called first which can be used to get the first element, the code has been updated with that.
You need to convert as Map and List because Flutter deal only with this datatypes. For example:
Map order1 = {
"_id":"624342e1c66be9001d501230",
"status":2,
"pinCode":749326,
"kioskId":"61bb3982089a66001db4ab77",
"kioskActivityId":"620668ad433322b99557c874"
};
Map order2 = {
"_id":"224342e1c66be9001d501232",
"status":1,
"pinCode":248023,
"kioskId":"41bb3982089a66001db4ab74",
"kioskActivityId":"720668ad433322b99557c875"
};
Map m = {
"amountCredited":0,
"isFirstOrder":false,
"orderItems":[
order1,
order2
]
};
print(m['orderItems'][1]);
print(m['orderItems'][1]['pinCode']);
The result will be:
{_id: 224342e1c66be9001d501232, status: 1, pinCode: 248023, kioskId: 41bb3982089a66001db4ab74, kioskActivityId: 720668ad433322b99557c875}
248023
Related
An API gives me json response like this:
[{"version": "v3.5"}, {"setup": true}, {"address": "e7d398b"}, {"connected": true}, {"active": true}, {"id": "ce7143"}, {"genuine": true}]
As you can see, this is a list of objects. I tried parsing it like this using quicktype generated model class-
List<Result>.from(result.map((x) => Result.fromJson(x)));
But it's failing since each of the objects are of different types.
I think I have to convert each of the objects in the array one by one to Dart classes and add it to an Array.
So I tried this (I am using dio) -
final result = response.data;
var b = List.empty(growable: true);
result.map((x) => b.add(x));
But it's not working.
How can I atleast access the elements of the array?
Solved
Inspired by the accepted answer, I was able to generate corresponding Dart Class. Never thought can looping through a map is possible, IDE was not giving any clue.
final result = response.data;
Map<String, dynamic> map = {};
for (var e in result) {
map.addAll(e);
}
final finalResult = Result.fromJson(map);
return finalResult;
As Randal Schwartz mentioned above, there is no JSON you can not parse with Dart.
In your case, you have a List of Map objects. What you can do is:
final data = jsonDecode(json) as List;
Map<String, dynamic> map = {};
for (var e in data) {
map.addAll(e);
}
print(map);
//prints
{version: v3.5, setup: true, address: e7d398b, connected: true, active: true, id: ce7143, genuine: true}
If you're using the dio flutter package it returns decoded json, no
need to call for jsonDecode.
I recommend using json code generation if you face large json instead of relying on quicktype generated models.
There's no JSON that isn't parsable with Dart. But you might end up with a data structure that requires careful navigation. Classes make it easier, but there isn't always a class structure for any arbitrary JSON, and maybe that's your point. In that case, you'll have to navigate to the data of interest in an ad-hoc fashion.
Recently I change all JSON of my app to show the errors, messages, and body of the service. In the body, I have an array of data. Before I change the JSON, all worked doing something like this:
final responseJson = json.decode(response.body);
Which returned:
[{"id":1,"descripcion":"Terreno Rio"},{"id":2,"descripcion":"Terreno
Asier"}]
Now I try to do something like this:
final responseJson = json.decode(response.body);
print(json.encode(responseJson));
Which returns:
[{"code":0,"message":"","body":[{"id":1,"descripcion":"Terreno
Rio"},{"id":2,"descripcion":"Terreno Asier"}]}]
Does anybody know the right way to extract some element of the JSON and decode?
I'm sure the JSON response that you get is like this:
{"code":0,"message":"","body":[{"id":1,"descripcion":"Terreno
Rio"},{"id":2,"descripcion":"Terreno Asier"}]}
So in order to parse that JSON, you can just access the body directly:
List list = responseJson['body'];
Now you can iterate through the elements of the array:
for (Map<String, dynamic> element in list) {
print(element);
}
You get a List of Maps. First access the first element (there is only 1) of the List with [0] and then the body element of the returned Map with ['body']:
var body = responseJson[0]['body'];
print(body);
I'm using the json_serializable pub package for all my RESTful API needs, and it worked great, but now i'm trying to serialize content from Wikipedia.
My problem is with the response structure, one of the keys is unknown beforehand and dynamic according to the returned page ID, here's an example:
I would like to get only the intro part of a page, my query is:
https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&redirects=1&exintro=1&explaintext=1&titles=Stack%20Overflow
The response will be:
{
"batchcomplete": "",
"query": {
"pages": {
"21721040": {
"pageid": 21721040,
"ns": 0,
"title": "Stack Overflow",
"extract": "Stack Overflow is a privately held website, the flagship site of ......."
}
}
}
}
Eventually i would like to get only the internal extract string, but "on my way" there i have the page id under pages.
I can't find how i can get an object when i cannot tell what is the key, neither using json_serializable package nor explicitly writing the JSON import code. Is it possible?
PS, i do think i found a way that required two API calls - if i add the indexpageids flag and set it to true, i will receive an additional dictionary entry called pageids with the page id numbers as string, then i can use the retrieved string in the second API call.
I still don't know the exact way i'll do it but i'm pretty sure it's possible, but sending 2 API requests every time is expensive and i would like to know if there's a more elegant way to do it.
Make sure that you use the latest version of json_serializable. Use a Map<String, Page> for key-value pairs:
#JsonSerializable()
class Query {
final Map<String, Page> pages;
Query(this.pages);
factory Query.fromJson(Map<String, dynamic> json) => _$QueryFromJson(json);
Map<String, dynamic> toJson() => _$QueryToJson(this);
}
Just to prove that it works, here is what json_serializable is generating:
Query _$QueryFromJson(Map<String, dynamic> json) {
return new Query((json['pages'] as Map<String, dynamic>)?.map((k, e) =>
new MapEntry(
k, e == null ? null : new Page.fromJson(e as Map<String, dynamic>))));
}
You can get keys of json & then use that key to fetch value as
var response = await http.get(API_ENDPOINT);
var responseJson = json.decode(response.body);
Map<String, dynamic> json =responseJson['query']['pages'];
String pageId = json.keys.toList()[0]; // 0 for first page, you can iterate for multiple pages
firstPage = json[pageId];
String title = firstPage["title"];
How would you do if you have another level of dynamic keys inside the dynamic key? I had to edit the generated json_serializable for my second class to use json only instead of the generated json['key'].
Page _$PageFromJson(Map<String, dynamic> json) {
return new Page((json as Map<String, dynamic>)?.map((k, e) =>
new MapEntry(
k, e == null ? null : new SecondPage.fromJson(e as Map<String, dynamic>))));
}
It feels like there is some better way? Because this generated code gets overwritten everytime I make changes in any file and run generator..
Im new to SO so I cant rate answers and write comments. But boformer's answer helped me but then I ran into this second question.
Edit: I found the solution by trial and error, used:
class Query {
final Map<String, Map<String, SecondPage>> pages;
I'm attempting to pull out data from a nested array in JSON but cannot seem to get the values correct. Right now, all values of the nested operatingSystem array print out in the table when I only need the name of the operating system. My code is below and please let me if you need more information.
Dart:
List<Map> assetList;
// LinkedHashMap preserves key entry order
LinkedHashMap<String, Map> dataMap = new LinkedHashMap<String, Map>();
for (var d in assetList) {
HashMap rowMap = new HashMap();
String domainId = d["process"]["processId"];
//first <td> element, the rest follow in succession
dataMap[domainId] = rowMap;
rowMap["domainId"] = domainId;
//is still not checking if null
if(d["asset"]["operatingSystem"].containsKey("name")){
rowMap["operatingSystem"] = d["asset"]["operatingSystem"]["name"];
} else{
rowMap["operatingSystem"] = d["asset"]["operatingSystem"];
}
//print out table data for debugging
print(rowMap.toString());
print(d);
JSON:
"asset":{
"assetId":"8a498592469189660146918d9e2f0000",
"oplock":0,
"domainName":"",
"latitude":58.92,
"ipAddress":"4.4.4.4",
"longitude":-37.23,
"operatingSystem":{
"osId":2,
"oplock":0,
"name":"Windows 8"
}
}
You need to go one level deeper. You are printing out the object's operatingSystem header but the operatingSystem header has 3 attributes.
The corect syntax is
json["asset"]["operatingSystem"]["name"];
You could also do it like this which I believe is more standard when it comes to JS and JSON
json.asset.operatingSystem.name
Please pardon me if this is a repeat question. I have been through some of the questions/answers with a similar requirement but somehow got a bit overwhelmed and confused at the same time. My requirement is:
I get a JSON string/object as a request parameter. ( eg: params.timesheetJSON )
I then have to parse/iterate through it.
Here is the JSON that my grails controller will be receiving:
{
"loginName":"user1",
"timesheetList":
[
{
"periodBegin":"2014/10/12",
"periodEnd":"2014/10/18",
"timesheetRows":[
{
"task":"Cleaning",
"description":"cleaning description",
"paycode":"payCode1"
},
{
"task":"painting",
"activityDescription":"painting description",
"paycode":"payCode2"
}
]
}
],
"overallStatus":"SUCCESS"
}
Questions:
How can I retrieve the whole JSON string from the request? Does request.JSON be fine here? If so, will request.JSON.timesheetJSON yield me the actual JSON that I want as a JSONObject?
What is the best way to parse through the JSON object that I got from the request? Is it grails.converters.JSON? Or is there any other easy way of parsing through? Like some API which will return the JSON as a collection of objects by automatically taking care of parsing. Or is programatically parsing through the JSON object the only way?
Like I said, please pardon me if the question is sounding vague. Any good references JSON parsing with grails might also be helpful here.
Edit: There's a change in the way I get the JSON string now. I get the JSON string as a request paramter.
String saveJSON // This holds the above JSON string.
def jsonObject = grails.converters.JSON.parse(saveJSON) // No problem here. Returns a JSONObject. I checked the class type.
def jsonArray = jsonArray.timesheetList // No problem here. Returns a JSONArray. I checked the class type.
println "*** Size of jsonArray1: " + jsonArray1.size() // Returns size 1. It seemed fine as the above JSON string had only one timesheet in timesheetList
def object1 = jsonArray[1] // This throws the JSONException, JSONArray[1] not found. I tried jsonArray.getJSONObject(1) and that throws the same exception.
Basically, I am looking to seamlessly iterate through the JSON string now.
I have wrote some code that explains how this can be done, that you can see below, but to be clear, first the answers to your questions:
Your JSON String as you wrote above will be the contents of your POST payload to the rest controller. Grails will use its data binding mechanism to bind the incomming data to a Command object that your should prepare. It has to have fields corresponding to the parameters in your JSON String (see below). After you bind your command object to your actual domain object, you can get all the data you want, by simply operating on fields and lists
The way to parse thru the JSON object is shown in my example below. The incomming request is esentially a nested map, with can be simply accessed with a dot
Now some code that illustrates how to do it.
In your controller create a method that accepts "YourCommand" object as input parameter:
def yourRestServiceMethod (YourCommand comm){
YourClass yourClass = new YourClass()
comm.bindTo(yourClass)
// do something with yourClass
// println yourClass.timeSheetList
}
The command looks like this:
class YourCommand {
String loginName
List<Map> timesheetList = []
String overallStatus
void bindTo(YourClass yourClass){
yourClass.loginName=loginName
yourClass.overallStatus=overallStatus
timesheetList.each { sheet ->
TimeSheet timeSheet = new TimeSheet()
timeSheet.periodBegin = sheet.periodBegin
timeSheet.periodEnd = sheet.periodEnd
sheet.timesheetRows.each { row ->
TimeSheetRow timeSheetRow = new TimeSheetRow()
timeSheetRow.task = row.task
timeSheetRow.description = row.description
timeSheetRow.paycode = row.paycode
timeSheet.timesheetRows.add(timeSheetRow)
}
yourClass.timeSheetList.add(timeSheet)
}
}
}
Its "bindTo" method is the key piece of logic that understands how to get parameters from the incomming request and map it to a regular object. That object is of type "YourClass" and it looks like this:
class YourClass {
String loginName
Collection<TimeSheet> timeSheetList = []
String overallStatus
}
all other classes that are part of that class:
class TimeSheet {
String periodBegin
String periodEnd
Collection<TimeSheetRow> timesheetRows = []
}
and the last one:
class TimeSheetRow {
String task
String description
String paycode
}
Hope this example is clear enough for you and answers your question
Edit: Extending the answer according to the new requirements
Looking at your new code, I see that you probably did some typos when writting that post
def jsonArray = jsonArray.timesheetList
should be:
def jsonArray = jsonObject.timesheetList
but you obviously have it properly in your code since otherwise it would not work, then the same with that line with "println":
jsonArray1.size()
shuold be:
jsonArray.size()
and the essential fix:
def object1 = jsonArray[1]
shuold be
def object1 = jsonArray[0]
your array is of size==1, the indexing starts with 0. // Can it be that easy? ;)
Then "object1" is again a JSONObject, so you can access the fields with a "." or as a map, for example like this:
object1.get('periodEnd')
I see your example contains errors, which lead you to implement more complex JSON parsing solutions.
I rewrite your sample to the working version. (At least now for Grails 3.x)
String saveJSON // This holds the above JSON string.
def jsonObject = grails.converters.JSON.parse(saveJSON)
println jsonObject.timesheetList // output timesheetList structure
println jsonObject.timesheetList[0].timesheetRows[1] // output second element of timesheetRows array: [paycode:payCode2, task:painting, activityDescription:painting description]