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

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

Related

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

I am trying to fetch data from a local json file in my assets. I received and decoded data and then tried to store it in a list for further use. But it give me the exception. I want the list to be of type OriginDestination, store it in database and then use it further. Can someone please tell me how can I parse data from json to OriginDestination.
Class OriginDestination -
OriginDestination cityDtoFromJson(String str) => OriginDestination.fromJson(json.decode(str));
String cityDtoToJson(OriginDestination data) => json.encode(data.toJson());
#HiveType(typeId: 0)
// ignore: non_constant_identifier_names
class OriginDestination extends HiveObject {
#HiveField(0)
City? origin;
#HiveField(1)
List<City>? destinations;
OriginDestination({
this.origin,
this.destinations,
});
factory OriginDestination.fromJson(Map<String, dynamic> json) => OriginDestination(
origin: City.fromJson(json["origin"]),
destinations: List<City>.from(
json["destinations"].map((x) => City.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"origin": origin,
"destinations": destinations,
};
Code where I am fetching data and want to use it (originDestinations is also a list of type OriginDestination) -
List<OriginDestination>? localOriginDestination = [];
Future<void> readJson() async {
final String response = await rootBundle.loadString('assets/files/node.json');
final data = await json.decode(response);
localOriginDestination = await data["data"].cast<OriginDestination>();
print(localOriginDestination);
if(localOriginDestination!=null) {
await _localStorageService.getBox().clear();
await _localStorageService.getBox().addAll(localOriginDestination!.toList());
originDestinations = _localStorageService.getOriginDestinations();
}
}
I have never used Hive before but this line of code seems suspicious to me:
localOriginDestination = await data["data"].cast<OriginDestination>();
I think you want to do something along the lines of:
localOriginDestination = [
for (final element in await data['data']) OriginDestination.fromJson(element),
];
But I can't be entirely certain without knowing what the value of await data['data'] is.
Edit, adding some more information.
The .cast method on List is only meant to be used with a type that is a subtype of the original list type, like in this example:
void main() {
List<num> nums = [1, 2, 3];
List<int> ints = nums.cast<int>();
ints.add(4);
print(ints);
}
What you are doing is essentially the same thing as this.
void main() {
List<Map<String, int>> items = [
{'A': 1, 'B': 2},
{'A': 3, 'B': 4},
];
final result = items.cast<ABC>();
print(result);
}
class ABC {
int a;
int b;
ABC.fromJson(Map<String, dynamic> json)
: a = json['A'],
b = json['B'];
}
The problem here is that the ABC class is not a subtype of Map<String, int>. Classes are not maps in the dart programming language. You need to need to call the .fromJson constructor on each element of the items list in order to get a List<ABC>.
You can do it in a few different ways.
Using map method is one approach. (you need to be on dart 2.15+ for this exact syntax)
List<ABC> result = items.map(ABC.fromJson).toList();
Looping over items with a collection for is another approach.
List<ABC> result = [for (final element in items) ABC.fromJson(element)];
Looping over items with a conventional for loop is yet another approach.
List<ABC> result = [];
for (final element in items) {
result.add(ABC.fromJson(element));
}

Flutter http get request json serialization

I would like to make a get requests and to convert results to a list of objects.
Actually i did that :
post_model.dart
#JsonSerializable()
class Post {
Post({
required this.userId,
required this.id,
required this.title,
required this.body,
});
factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
Map<String, dynamic> toJson() => _$PostToJson(this);
final int userId;
final int id;
final String title;
final String body;
}
http_service.dart :
class HttpService {
final String url = 'https://jsonplaceholder.typicode.com';
final String postsURL = '/posts';
final Map<String, String> headers = {
'Content-Type': 'application/json'
};
List<Post> parsePosts(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Post>((json) => Post.fromJson(json)).toList();
}
Future<List<Post>> fetchPosts() async {
final http.Response response = await http.get(Uri.https(url, postsURL));
if (response.statusCode == 200) {
return compute(parsePosts,response.body);
} else {
throw Exception("Failed to load posts ${response.statusCode}");
}
}
}
But i encouter all in red on the parsePosts method.
On the final parsed :
Missing variable type for 'parsed'.
Try adding an explicit type, or remove implicit-dynamic from your analysis options file.dart(implicit_dynamic_variable)
and on the return :
A value of type 'dynamic' can't be returned from the method 'parsePosts' because it has a return type of 'List<Post>'
I just don't understand what i'm doing wrong, because i have follow the flutter doc here :
https://flutter.dev/docs/cookbook/networking/background-parsing
Thanks for any help
Some time ago I did this code for that API:
import 'dart:async';
import 'package:wnetworking/wnetworking.dart';
class Post {
int? userId, id;
String? title, body;
Post.fromJson(Map<String, dynamic> data) {
userId = data['userId'];
id = data['id'];
title = data['title'];
body = data['body'];
}
}
class JsonPlaceHolder {
static const baseUrl = 'https://jsonplaceholder.typicode.com';
/* ---------------------------------------------------------------------------- */
static FutureOr<void> _doGet(String path, {void doThis(var response)?}) async {
await HttpReqService.getJson(baseUrl + path)
.then((response) => doThis == null ? print(response) : doThis(response))
.whenComplete(() => print('\nFetching done!'));
}
/* ---------------------------------------------------------------------------- */
static FutureOr<void> _doPost(String path, {required Object body, int okCode = 200}) async {
await HttpReqService.post<Map<String, dynamic>>(baseUrl + path, body: body, okCode: okCode)
.then((response) => print(response))
.whenComplete(() => print('\nPost sent successfully'));
}
/* ---------------------------------------------------------------------------- */
static FutureOr<void> fetchPosts({int? id, bool onlyComments = false, bool useObjList = false}) async {
var path = '/posts/${id ?? ''}';
if (id != null && onlyComments) path += '/comments';
useObjList
? await _doGet(path, doThis: (response) {
if (response != null) {
print((response as List).map<Post>((m) => Post.fromJson(m as Map<String, dynamic>)));
}
})
: await _doGet(path);
}
/* ---------------------------------------------------------------------------- */
static FutureOr<void> fetchComments([int? postId]) => _doGet('/comments${postId != null ? '?postId='+postId.toString() : ''}');
static FutureOr<void> fetchAlbums() => _doGet('/albums');
static FutureOr<void> fetchPhotos() => _doGet('/photos');
static FutureOr<void> fetchTodos() => _doGet('/todos');
static FutureOr<void> fetchUsers() => _doGet('/users');
}
void main(List<String> args) async {
// await JsonPlaceHolder.fetchComments(1);
await JsonPlaceHolder.fetchPosts(useObjList: true);
print('Finished!');
}
Result:
(Instance of 'Post', Instance of 'Post', Instance of 'Post', ..., Instance of 'Post', Instance of 'Post')
Fetching done!
Finished!
Note
wnetworking package is not ready to publish yet, it contains operations related to API, etc. You can replace HttpReqService.getJson and HttpReqService.post with your typical http.get and http.post respectively but keep in mind the return value and exceptions.
Use Uri.parse
Those errors are due to a dart version.
A fix has been made by another user on github : https://github.com/flutter/website/pull/5798
Following this PR, it's works

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 - unable to parse json array

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));

Flutter: InternalLinkedHashMap<String, dynamic>' has no instance method 'cast' with matching arguments

I'm unable to find solutions from the previously available question, I have cast json string to map
Below is my API calling method.
Future<EventResponse> fetchEvent( ) async { // here i change Future type
String url='http://xxxxxxxxxxxx.tk/api/userapp/event/lists';
var headers = new Map<String, String>();//here i defined Map type
headers['Auth-Key'] = 'OCDOC#2018';
headers['End-Client'] = 'OCDOC';
var body = new Map<String, String>();//here i defined Map type
headers['schedule'] = 'present';
http.Response res = await http.post(url,headers: headers, body: body);
final parsed=json.decode(res.body);
var myMap = Map<String, dynamic>.from(parsed);
EventResponse eventResponse = EventResponse.convertEventResponse(myMap);
return eventResponse;
}
this is my convertEventResponse methode
factory EventResponse.convertEventResponse(Map<String, dynamic> json) {
List<dynamic> events = json['eventList'];
List<Event> eventList = events.map((e) => Event.convertEvent(e)).toList(); //here i changed by #Richard Heap answer
return EventResponse(
error: json['error'],
status: json['status'],
deliveryCharges: json['deliveryCharge'],
imageBaseUrl: json['image_base_url'],
imageLogoUrl: json['image_logo_url'],
eventList: eventList,
);
}
The error i'm getting.
InternalLinkedHashMap<String, dynamic>' has no instance method 'cast' with matching arguments.
Use instead
.cast<String,dynamic>();
See also https://api.dartlang.org/stable/2.0.0/dart-core/Map/cast.html
Usually it's better to use Map<String,String>.from(oldMap) instead of cast<...>(...)

Categories

Resources