Flutter - unable to parse json array - android

I want to fetch list of cities from rest webservice, this way:
Future<List<City>> fetchCities() async {
final response =
await http.get('https://my-url/city',
headers: {HttpHeaders.acceptHeader: "application/json"});
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return compute(parseCities, response.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load cities');
}
}
Then to parse:
List<City> parseCities(String responseBody) {
final parsed = json.decode(responseBody)['data']['children'].cast<Map<String, dynamic>>();
return parsed.map<City>((json) => City.fromJson(json['data'])).toList();
}
And this is City class definition:
class City {
final String id;
final String name;
City({this.id, this.name});
factory City.fromJson(Map<String, dynamic> json) {
return City(
id: json['id'],
name: json['name'],
);
}
}
My example responseBody is:
[{\"id\":\"599\",\"name\":\"Bia\u0142ystok-dev\",\"location\":{\"long\":\"23.15\",\"lat\":\"53.13\"}}]
(for now, I want to ommit location and only fetch id and name). My json.decode throws exception:
type 'String' is not a subtype of type 'int' of 'index'
How to fix that? Any ideas?

The json string you have posted does not contain the keys data or children so to parse that json you would need to change your parse method to the following:
List<City> parseCities(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<City>((json) => City.fromJson(json)).toList();
}
although I think its good practice to use List.from instead of .cast
final parsed = List<Map<String, dynamic>>.from(json.decode(responseBody));

Related

Flutter - How to parse nested JSON structures with lists in flutter?

Here is my JSON
{
"type-1":[
{
"name": "type-1-1"
},
{
"name": "type-1-2"
}
],
"type-2": [
{
"name": "type-2-1"
},
{
"name": "type-2-2"
},
{
"name": "type-2-3"
}
]
}
I want to parse this JSON so for that I have written my model like this
class Places {
final List<PlaceName> names;
Places({this.names});
factory Places.fromJson(Map<String, dynamic> parsedJson){
var list = parsedJson['name'] as List;
List<PlaceName> placeList = list.map((i) => PlaceName.fromJson(i)).toList();
return Places(
names: placeList
);
}
}
class PlaceName {
final String name;
PlaceName({this.name});
factory PlaceName.fromJson(Map<String, dynamic> parsedJson){
return PlaceName(
name : parsedJson['name'],
);
}
}
and lastly, I have written my function in widget tree like this
Future<String> _loadAllPlaces() async {
return await rootBundle.loadString('assets/location.json');
}
Future loadPlaces() async {
String jsonString = await _loadAllPlaces();
final jsonResponse = json.decode(jsonString);
PlaceName place = new PlaceName.fromJson(jsonResponse);
print(place);
print(place.name);
return place;
}
So when I call loadPlaces() in FutureBuilder its returning null. I tried debugging it but no luck. Please help. Thanks in advance.
In your Places.fromJson method at the line var list = parsedJson['name'] you are trying to access the list inside the objects before referencing the object itself. Instead you should pass the name of the one of the objects like type-1 by example, in this way var list = parsedJson['type-1'] as List;.
But this would return just the first object's list. To get all the objects and the list of names of each one, you need iterate over the json objects , which in that example are type-1 and type-2, and then iterate over its lists. You can check the iteration in AllPlaces.fromJson. So you need to do some changes in your code to get all the objects from the json. You can try something like the code bellow, that you can test at DartPad
import 'dart:convert';
String jsonString = '{"type-1":[{"name": "type-1-1"},{"name": "type-1-2"}],"type-2": [{"name": "type-2-1"},{"name": "type-2-2"},{"name": "type-2-3"}]}';
class AllPlaces {
List<PlaceName> placeNames;
AllPlaces({this.placeNames});
AllPlaces.fromJson(Map<String, dynamic> json) {
placeNames = new List<PlaceName>();
//Iterates over the json objects.
json.forEach((type, list) {
//Iterates over the list of each object.
json[type].forEach((list) {
//Creates an instance of PlaceName and adds to the placeNames list.
placeNames.add(new PlaceName.fromJson(list));
});
});
}
}
class PlaceName {
String name;
PlaceName({this.name});
PlaceName.fromJson(Map<String, dynamic> json) {
name = json['name'];
}
}
Future loadPlaces() async {
//String jsonString = await _loadAllPlaces();
final jsonResponse = json.decode(jsonString);
AllPlaces allPlaces = new AllPlaces.fromJson(jsonResponse);
//Print the names of each object in the console
allPlaces.placeNames.forEach((place) => print(place.name));
return allPlaces;
}
void main() {
loadPlaces();
}

Exception: _InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'int' in type cast

In my flutter application, I AM PREPARING data to be sent via a REST call. Below is my code.
Future<void> saveOrderItemList(List<OrderItem> orderItems) async {
int responsetag = 0;
try {
List jsonList = OrderItem.encondeToJson(orderItems);
await http.post(_navLinks.saveOrderItems(),
body: jsonList,
headers: {
"Accept": "application/json",
"content-type": "application/json"
}).then((http.Response response) {
final int statusCode = response.statusCode;
print("RESPONSE: " + response.body);
print("STATUS CODE: " + statusCode.toString());
if (statusCode < 200 || statusCode > 400 || response.body == null) {
throw new Exception("Error while fetching data");
} else {
responsetag = int.parse(response.body);
}
});
return responsetag;
} catch (error) {
throw (error);
}
}
Below code shows the OrderItem model class
part 'order_item.g.dart';
/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
#JsonSerializable()
class OrderItem {
int idorderItem;
FreshProducts freshProducts;
Order order;
ProductSize productSize;
double orderItemExpectedPricePerKg;
double orderItemQuantity;
int dateCreated;
int lastUpdated;
OrderItem(
{this.idorderItem,
this.freshProducts,
this.order,
this.productSize,
this.orderItemExpectedPricePerKg,
this.orderItemQuantity,
this.dateCreated,
this.lastUpdated});
/// A necessary factory constructor for creating a new instance
/// from a map. Pass the map to the generated `_$OrderItemFromJson()` constructor.
/// The constructor is named after the source class, in this case User.
factory OrderItem.fromJson(Map<String, dynamic> json) =>
_$OrderItemFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$OrderItemToJson`.
Map<String, dynamic> toJson() => _$OrderItemToJson(this);
static List encondeToJson(List<OrderItem> list) {
List jsonList = List();
list.map((item) => jsonList.add(item.toJson())).toList();
return jsonList;
}
}
Below code shows the JSOB Serialization generated for the model class
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'order_item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
OrderItem _$OrderItemFromJson(Map<String, dynamic> json) {
return OrderItem(
idorderItem: json['idorderItem'] as int,
freshProducts: json['freshProducts'] == null
? null
: FreshProducts.fromJson(json['freshProducts'] as Map<String, dynamic>),
order: json['order'] == null
? null
: Order.fromJson(json['order'] as Map<String, dynamic>),
productSize: json['productSize'] == null
? null
: ProductSize.fromJson(json['productSize'] as Map<String, dynamic>),
orderItemExpectedPricePerKg:
(json['orderItemExpectedPricePerKg'] as num)?.toDouble(),
orderItemQuantity: (json['orderItemQuantity'] as num)?.toDouble(),
dateCreated: json['dateCreated'] as int,
lastUpdated: json['lastUpdated'] as int,
);
}
Map<String, dynamic> _$OrderItemToJson(OrderItem instance) => <String, dynamic>{
'idorderItem': instance.idorderItem,
'freshProducts': instance.freshProducts,
'order': instance.order,
'productSize': instance.productSize,
'orderItemExpectedPricePerKg': instance.orderItemExpectedPricePerKg,
'orderItemQuantity': instance.orderItemQuantity,
'dateCreated': instance.dateCreated,
'lastUpdated': instance.lastUpdated,
};
However when I run my saveOrderItemList function, I get the following error.
I/flutter (23028): type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'int' in type cast
Why is this happening?

Flutter: How to JSON encode a `List` properly?

I am trying to pass a List to my server using flutter, via a REST API.
Below is the code
Future<void> saveOrderItemList(List<OrderItem> orderItems) async {
int responsetag = 0;
try {
await http.post("http://url to post the data",
body: convert.json.encode(orderItems.toJson()), //This line do not work
headers: {
"Accept": "application/json",
"content-type": "application/json"
}).then((http.Response response) {
final int statusCode = response.statusCode;
print("RESPONSE: " + response.body);
print("STATUS CODE: " + statusCode.toString());
if (statusCode < 200 || statusCode > 400 || response.body == null) {
throw new Exception("Error while fetching data");
} else {
responsetag = int.parse(response.body);
}
});
return responsetag;
} catch (error) {
throw (error);
}
}
The above code doesn't run because I can't encode a List using convert.json.encode(orderItems.toJson()).
Below is my code for OrderItem bean and its serialisation class.
part 'order_item.g.dart';
/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
#JsonSerializable()
class OrderItem {
int idorderItem;
FreshProducts freshProducts;
Order order;
ProductSize productSize;
double orderItemExpectedPricePerKg;
double orderItemQuantity;
int dateCreated;
int lastUpdated;
OrderItem(
{
this.idorderItem,
this.freshProducts,
this.order,
this.productSize,
this.orderItemExpectedPricePerKg,
this.orderItemQuantity,
this.dateCreated,
this.lastUpdated
});
/// A necessary factory constructor for creating a new User instance
/// from a map. Pass the map to the generated `_$OrderItemFromJson()` constructor.
/// The constructor is named after the source class, in this case User.
factory OrderItem.fromJson(Map<String, dynamic> json) => _$OrderItemFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$OrderItemToJson`.
Map<String, dynamic> toJson() => _$OrderItemToJson(this);
}
And
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'order_item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
OrderItem _$OrderItemFromJson(Map<String, dynamic> json) {
return OrderItem(
idorderItem: json['idorderItem'] as int,
freshProducts: json['freshProducts'] == null
? null
: FreshProducts.fromJson(json['freshProducts'] as Map<String, dynamic>),
order: json['order'] == null
? null
: Order.fromJson(json['order'] as Map<String, dynamic>),
productSize: json['productSize'] == null
? null
: ProductSize.fromJson(json['productSize'] as Map<String, dynamic>),
orderItemExpectedPricePerKg:
(json['orderItemExpectedPricePerKg'] as num)?.toDouble(),
orderItemQuantity: (json['orderItemQuantity'] as num)?.toDouble(),
dateCreated: json['dateCreated'] as int,
lastUpdated: json['lastUpdated'] as int,
);
}
Map<String, dynamic> _$OrderItemToJson(OrderItem instance) => <String, dynamic>{
'idorderItem': instance.idorderItem,
'freshProducts': instance.freshProducts,
'order': instance.order,
'productSize': instance.productSize,
'orderItemExpectedPricePerKg': instance.orderItemExpectedPricePerKg,
'orderItemQuantity': instance.orderItemQuantity,
'dateCreated': instance.dateCreated,
'lastUpdated': instance.lastUpdated,
};
how can I make sure I Can pass a list to POST from flutter http?
I solved a similar type of problem. But my List was wrap with a other keys like {"data":listOfItemJson} . So I first created a Map like that var map = {"data": MainOrderController.orderModel.toJson()}; and when i post this the uril i updated post mechanism like :
var response =
await http.post(url, headers: headers, body: jsonEncode(map));
Note: jsonEncode() is part of import 'dart:convert'; package.
I hope you can get an Idea
My take on this would be to replace json_serializable library with https://github.com/k-paxian/dart-json-mapper
It's quite similar, but don't over-bloats your code with boilerplate and not forcing you to maintain loads of '*.g.part' files, etc.
It will help you not only for this case, but for all Dart Object => JSON => Dart Object cases.
Please carefully read library readme document first, especially section "Basic setup".
With dart-json-mapper your code could look like this:
import 'main.reflectable.dart' show initializeReflectable;
import 'package:dart_json_mapper/dart_json_mapper.dart' show jsonSerializable, JsonMapper;
#jsonSerializable
#Json(enumValues: ProductSize.values)
enum ProductSize {Big, Small}
#jsonSerializable
class Order {
// ...
}
#jsonSerializable
class FreshProducts {
// ...
}
#jsonSerializable
class OrderItem {
int idorderItem;
FreshProducts freshProducts;
Order order;
ProductSize productSize;
double orderItemExpectedPricePerKg;
double orderItemQuantity;
int dateCreated;
int lastUpdated;
OrderItem(
{
this.idorderItem,
this.freshProducts,
this.order,
this.productSize,
this.orderItemExpectedPricePerKg,
this.orderItemQuantity,
this.dateCreated,
this.lastUpdated
});
}
void main {
initializeReflectable();
final orderItems = <OrderItem>[OrderItem(), OrderItem(), OrderItem()];
// Instead of convert.json.encode(orderItems.toJson())
final orderItemsJson = JsonMapper.serialize(orderItems);
// orderItemsJson will have Json string which you could pass as body to your post request
}
According json_serializable and https://github.com/google/json_serializable.dart/issues/651 ,you can put the following to build.yaml in root folder. The explicit_to_json default is false, set it true and it will explicitly call the nested toJson.
targets:
$default:
builders:
json_serializable:
options:
# Options configure how source code is generated for every
# `#JsonSerializable`-annotated class in the package.
#
# The default value for each is listed.
explicit_to_json: true

Flutter Error : Exception: type 'int' is not a subtype of type 'String'

How it be happend ? i think it happend when i doin json mapping. it said Exception: type 'int' is not a subtype of type 'String'. I've tried using local json assets file but it not make different. Please help me with this problem.
class Product {
int productId;
String productName;
String productPrice;
Product({this.productId, this.productName, this.productPrice});
Product.fromProduct(Product p) {
this.productId = p.productId;
this.productName = p.productName;
this.productPrice = p.productPrice;
}
factory Product.fromJson(Map<String, dynamic> parsedJson) {
return Product(
productId: parsedJson['ID'],
productName: parsedJson['Name'],
productPrice: parsedJson['SellPrice']);
}
}
Future<String> _loadAProductAsset() async {
var res = await http.get(Uri.encodeFull("http://10.0.2.2:9155/product"));
return json.encode(json.decode(res.body)["data"]);
}
List<Product> parseProduct(String myJson) {
final parsed = json.decode(myJson).cast<Map<String, dynamic>>();
return parsed.map<Product>((json) => Product.fromJson(json)).toList();
}
Future<List<Product>> fetchProduct() async {
await wait(1);
String jsonString = await _loadAProductAsset();
return compute(parseProduct, jsonString);
}
Future wait(int s) {
return new Future.delayed(Duration(seconds: s), () => {});
}
this is my json from _loadAProductAsset() function
[
{
"ID": 2,
"CreatedAt": "2020-01-06T03:56:32+07:00",
"UpdatedAt": "2020-01-06T03:56:32+07:00",
"DeletedAt": null,
"Name": "Product A",
"Category": "0",
"Stock": "50",
"StockUnit": "0",
"BuyPrice": "20000",
"SellPrice": "21000",
"SupplierID": "1"
}
]
Solution 1: Instead of explicitly defining the data types as string and ints you could define them as dynamics as follow:
dynamic productId;
dynamic productName;
dynamic productPrice;
What happens here is that you are giving the responsibility to dart of taking care of casting whatever data type that comes in.
Solution 2: Check the structure of incoming JSON by going to the link in your browser window and seeing what is the Data type for each set of data that is coming in. For eg. seeing what is the type of productId. If it is stated as "12" then it must be a string.
After concluding the data type of your JSON items, you could parse the data while deserialising the JSON and defining the variables in the factory constructor(fromProduct in your case). This goes as follows:
Product.fromProduct(Product p) {
this.productId = int.parse(p.productId); // takes in a String and converts it into an int.
this.productName = p.productName;
this.productPrice = p.productPrice;
}
You need to parse the String that comes from JSON into an int:
Product.fromProduct(Product p) {
this.productId = int.parse(p.productId);
this.productName = p.productName;
this.productPrice = p.productPrice;
}
For some reason I had this issue and I already did json.decode(response) earlier but I had to do it again just before doing MyModel.fromJson(response)
So what I typically recommend is
import 'dart:convert';
.
.
.
.
var decodedJson = json.decode(decode);
MyModel model = MyModel.fromJson(decodedJson);
The above worked for me.

Error in json parsing on flutter. type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'ResultData' in type cast

I have json as below.
{
"result_data":{
"id":"b57457b0-4f73-11e8-92ae-01912016d38c",
"rates":[
{ },
{ }
]
}
}
I want to parse and below is my code to parse it.
To get the "result_data"
class RootModel {
ResultData resultData;
RootModel(this.resultData);
RootModel.fromJSON(Map<String, dynamic> response) {
var list = response['result_data'] as ResultData;
print(list.runtimeType);
}
}
To parse the result data object.
class ResultData {
String id;
List<Rate> rates;
ResultData(this.id, this.rates);
ResultData.fromJSON(Map<String, dynamic> parsedJson) {
this.id = parsedJson['id'] as String;
this.rates = (parsedJson['rates'] as List)
.map((json) => Rate.fromJSON(json))
.toList();
}
}
Further rates model has also other models and list of models.
The error I am getting for the above code is:
E/flutter: [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'ResultData' in type cast
#0 new RootModel.fromJSON (package:movies_streams/models/RootModel.dart:16:40)
You can't cast map to ResultData.
class RootModel {
ResultData resultData;
RootModel(this.resultData);
RootModel.fromJSON(Map<String, dynamic> response) {
// var list = response['result_data'] as ResultData;
var list = ResultData.fromJSON(response['result_data']);
print(list.runtimeType);
}
}
There is no magic happening for JSON deserialization. You have to invoke the fromJSON yourself, instead of just casting:
class RootModel {
ResultData resultData;
RootModel(this.resultData);
RootModel.fromJSON(Map<String, dynamic> response)
: resultData = ResultData.fromJSON(response['result_data'] as Map<String, dynamic>);
}

Categories

Resources