I am new to Dart and trying to deserialize some json, but I can't quite figure out the trick.
I am able to cast the Map in main as shown, but the commented code does not work and I can't figure out why. I feel like there must be an easier way to do this (outside of using a package for code generation), but I'm not sure what that is.
import 'dart:convert';
import 'dart:io';
class Book {
final String title;
final String path;
Book(this.title, this.path);
Book.fromJson(Map<String, dynamic> content)
: title = content['title'],
path = content['path'];
Map<String, dynamic> toJson() =>
{
'title': title,
'path': path,
};
String toString() {
return '{ title: $title, path: $path }';
}
}
class Books {
final Map<String, Book> bookList;
Books(this.bookList);
// Books.fromJson(Map<String, dynamic> content)
// : bookList = Map<String, Book>.from(jsonDecode(jsonDecode(content)['books']).map((k,v) => MapEntry(k as String, Book.fromJson(v))));
Map<String, dynamic> toJson() =>
{
'books': jsonEncode(bookList),
};
String toString() {
return bookList.toString();
}
}
void main() {
Map<String, Book> bookList = {
"foo": Book("Foo", "/foo"),
"bar": Book("Bar", "/bar"),
};
Books books = Books(bookList);
print(books);
String content = jsonEncode(books);
print(content);
// print(Books.fromJson(jsonDecode(content)));
Map<String, Book> m = Map<String, Book>.from(jsonDecode(jsonDecode(content)['books']).map((k,v) => MapEntry(k as String, Book.fromJson(v))));
print(m);
}
Oops, I needed to remove an invocation of jsonDecode from Books.fromJson...
Books.fromJson(Map<String, dynamic> content)
: bookList = Map<String, Book>.from(jsonDecode((content)['books']).map((k,v) => MapEntry(k as String, Book.fromJson(v))));
Related
Did this according to this manual
https://www.bezkoder.com/dart-flutter-parse-json-string-array-to-object-list/
Dart/Flutter parse array of JSON objects into List
JSON that comes from the server
{"myChannels":[{"id":"2","name":"channel2test","imageUrl":"image1.png"},{"id":"2","name":"channel2test","imageUrl":"image2.png"}]}
Model Class
class ChannelModel {
String channelID;
String channelName;
String imageUrl;
ChannelModel(this.channelID, this.channelName, this.imageUrl);
factory ChannelModel.parsingChannels(dynamic json) {
return ChannelModel(json['channelID'] as String,
json['channelName'] as String, json['imageUrl'] as String);
}
#override
String toString() {
return '{ ${this.channelID}, ${this.channelName}, ${this.imageUrl} }';
}
}
Main block
try {
final response = await http.post(
url,
body: json.encode({
'action': 'getMyChannels',
'userID': userID,
'returnSecureToken': true,
}),
);
// print(jsonDecode(response.body));
var extractedData =
jsonDecode(response.body)['myChannels'] as List<dynamic>;
List<ChannelModel> channelObjects = extractedData
.map((cJson) => ChannelModel.parsingChannels(cJson))
.toList();
print(channelObjects);
channelObjects.forEach((Data) {
print('test');
});
the results is the following...
print(channelObjects) > outputs > []
print('test') > not working , channelObjects not looping
I would suggest serializing your response to dart classes. It would be much easier to access the data you want.
class ApiResponse {
ApiResponse({
required this.myChannels,
});
List<MyChannel> myChannels;
factory ApiResponse.fromJson(Map<String, dynamic> json) => ApiResponse(
myChannels: List<MyChannel>.from(
json["myChannels"].map((x) => MyChannel.fromJson(x)),
),
);
}
class MyChannel {
MyChannel({
required this.id,
required this.name,
required this.imageUrl,
});
String id;
String name;
String imageUrl;
factory MyChannel.fromJson(Map<String, dynamic> json) => MyChannel(
id: json["id"],
name: json["name"],
imageUrl: json["imageUrl"],
);
}
Then you can use it this way :
final extractedData = ApiResponse.fromJson(json.decode(response.body) as Map<String, dynamic>);
And access the data you want
extractedData.myChannels[0].id
extractedData.myChannels[0].name
extractedData.myChannels[0].imageUrl
for (var ch in extractedData.myChannels){
print(ch.id);
print(ch.name);
print(ch.imageUrl);
}
part 'example.g.dart';
#JsonSerializable()
class Channel{
final String id;
final String name;
final String? imageUrl;
Channel({required this.id, required this.name, this.imageUrl});
factory Channel.fromJson(Map<String, dynamic> json) => _$ChannelFromJson(json);
Map<String, dynamic> toJson() => _$ChannelToJson(this);
}
#JsonSerializable()
class ChannelList{
final List<Channel> myChannels;
ChannelList({required this.myChannels});
factory ChannelList.fromJson(Map<String, dynamic> json) => _$ChannelListFromJson(json);
Map<String, dynamic> toJson() => _$ChannelListToJson(this);
}
final extractedData = ChannelList.fromJson(json.decode(response.body));
https://pub.dev/packages/json_serializable
Using JsonSerializable more useful and understandable. You can read documentation of that package
Im sure you know the problem above. Im wondering how I can solve it. I understand that my data is in form of a list but inside the data class I used map. I don't really understand how I should change it to work, basically I just followed the flutter.dev documentation
So if you are wondering what I did
I basically parsed my data with json_serializable. In testing with test data all worked fine.
My data:
My model contains a title, image & a nested class called modelData.
`import 'package:json_annotation/json_annotation.dart';
import 'modelData.dart';
part 'Modell.g.dart';
#JsonSerializable(explicitToJson: true)
class Modell {
final String title;
final String image;
final ModelData modelData;
Modell(this.title, this.image, this.modelData);
factory Modell.fromJson(Map<String, dynamic> json)
=> _$ModellFromJson(json);
Map<String, dynamic> toJson() => _$ModellToJson(this);}
`
import 'package:json_annotation/json_annotation.dart';
part 'modelData.g.dart';
#JsonSerializable()
class ModelData {
final String title;
ModelData(this.title);
factory ModelData.fromJson(Map<String, dynamic> json)
=> _$ModelDataFromJson(json);
Map<String, dynamic> toJson() => _$ModelDataToJson(this);
}
Im consuming the data with this code:
var modelle = const[];
Future loadDataList() async {
String content = await rootBundle.loadString("assets/ddddddd.json");
List collection = json.decode(content);
List<Modell> _modelle = collection.map((json) => Modell.fromJson(json)).toList();
setState(() {
modelle = _modelle;
});
}
void initState() {
loadDataList();
super.initState();
& if needed here a part of my Data:
[
{
"title":" Alfa Romeo ",
"image":" AlfaRomeo.png ",
"modelData":[
{
"title":" 4C ",
"variantenData":[
]
},
I hope I wrote clear & detailed enough. If Im missing something, sry
-----Update-----
I tested a bit & found out that with simply adding List I no longer get the error, will test further to see whether the solution really works as intended
import 'package:json_annotation/json_annotation.dart';
import 'modelData.dart';
part 'Modell.g.dart';
#JsonSerializable(explicitToJson: true)
class Modell {
final String title;
final String image;
List <ModelData> modelData; //adding List
Modell(this.title, this.image, this.modelData);
factory Modell.fromJson(Map<String, dynamic> json)
=> _$ModellFromJson(json);
Map<String, dynamic> toJson() => _$ModellToJson(this);
}
You have parsed the data and all but the class named Modell should contain a list of ModelData and not a single ModelData. Please change the type to a list of ModelData and it should work hopefully.
class Modell {
final String title;
final String image;
final List<ModelData> modelDataList;
Modell(this.title, this.image, this.modelDataList);
factory Modell.fromJson(Map<String, dynamic> json)
=> _$ModellFromJson(json);
Map<String, dynamic> toJson() => _$ModellToJson(this);}```
In this part:
var modelle = const[];
Future loadDataList() async {
String content = await rootBundle.loadString("assets/ddddddd.json");
List collection = json.decode(content);
List<Modell> _modelle = collection.map((json) => Modell.fromJson(json)).toList();
setState(() {
modelle = _modelle;
});
}
void initState() {
loadDataList();
super.initState();
More specific here:
List collection = json.decode(content);
If you go to Flutter Example will see that collection will return a Map<String, dynamic>.
Try change to:
Map<String, dynamic> collection = json.decode(content);
I'm quite new to flutter and right now i'm stucked in desterilize the json string into my class.
Appreciate your help on this.
This is my json
[
{
"itemno": "4800888136473",
"itemname": "AXE DEO AFRICA 150ML",
},
{
"itemno": "4800888141125",
"itemname": "AXE DEO BODYSPRAY DARK TMPTTN 150ML",
}
]
And my JSON Class
class ListItemList{
ListItemList({
this.itemno,
this.itemname,
});
String itemno;
String itemname;
factory ListItemList.fromJson(Map<String, dynamic> json) =>
ListItemList(
itemno: json["itemno"],
itemname: json["itemname"],
);
}
How i call
List<ListItemList> result =
ListItemList.fromJson(jsonDecode(response.body));
Check this link "https://app.quicktype.io/"
And paste your json code left side and add class model name.
for eg.
import 'dart:convert';
List<User> userFromJson(String str) => List<User>.from(json.decode(str).map((x) => User.fromJson(x)));
String userToJson(List<User> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class User {
User({
this.itemno,
this.itemname,
});
String itemno;
String itemname;
factory User.fromJson(Map<String, dynamic> json) => User(
itemno: json["itemno"] == null ? null : json["itemno"],
itemname: json["itemname"] == null ? null : json["itemname"],
);
Map<String, dynamic> toJson() => {
"itemno": itemno == null ? null : itemno,
"itemname": itemname == null ? null : itemname,
};
}
//Add below code in service
static Future<List<User>> getUsers() async {
List<User> users = usersFromJson(response.body));
return users;
}
// call service in specific page
List _users;
#override
void initState() {
super.initState();
ApiService.getUsers().then((value) {
setState(() {
_users = value;
});
})
}
Use map to iterate over the JSON which is a list.
final List<ListItemList> result = (jsonDecode(response.body) as List)
.map((e) => ListItemList.fromJson(e))
.toList();
Go to this URL and paste your JSON. It will convert it to both fromJson (json to dart object conversion) and toJson (dart object to json conversion) function.
Here is as example of fromJson and toJosn according to the Json you provided
class List {
List<Items> items;
List({this.items});
List.fromJson(Map<String, dynamic> json) {
if (json['items'] != null) {
items = new List<Items>();
json['items'].forEach((v) {
items.add(new Items.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.items != null) {
data['items'] = this.items.map((v) => v.toJson()).toList();
}
return data;
}
}
class Items {
String itemno;
String itemname;
Items({this.itemno, this.itemname});
Items.fromJson(Map<String, dynamic> json) {
itemno = json['itemno'];
itemname = json['itemname'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['itemno'] = this.itemno;
data['itemname'] = this.itemname;
return data;
}
}
As i mentioned it is a list. Deal with list like;
List<ListItemList> result;
var a = jsonDecode(response.body);
// I can not compile this part you can check syntax
a.forEach((element)
at= ListItemList.fromJson(element);
result.add(at);
);
Trying to convert my json to objects in Dart/Flutter using the json_serializable. I cannot seem to find a way to access a nested ID (data is coming from MongoDB thus the $ in the json).
Here is the json:
{
"_id": {
"$oid": "5c00b227" <-- this is what I am trying to access
},
"base": 1,
"tax": 1,
"minimum": 5,
"type": "blah"
}
Result:
class Thing {
final int id;
final String base;
final String tax;
final String type;
final int minimum;
}
It is not possible with json_serializable package itself. You have to create separate objects for getting this nested data.
Look the discussion here
https://github.com/google/json_serializable.dart/issues/490
But, there is possible way to get nested fields with added converter (solution was found here https://github.com/google/json_serializable.dart/blob/master/example/lib/nested_values_example.dart)
import 'package:json_annotation/json_annotation.dart';
part 'nested_values_example.g.dart';
/// An example work-around for
/// https://github.com/google/json_serializable.dart/issues/490
#JsonSerializable()
class NestedValueExample {
NestedValueExample(this.nestedValues);
factory NestedValueExample.fromJson(Map<String, dynamic> json) =>
_$NestedValueExampleFromJson(json);
#_NestedListConverter()
#JsonKey(name: 'root_items')
final List<String> nestedValues;
Map<String, dynamic> toJson() => _$NestedValueExampleToJson(this);
}
class _NestedListConverter
extends JsonConverter<List<String>, Map<String, dynamic>> {
const _NestedListConverter();
#override
List<String> fromJson(Map<String, dynamic> json) => [
for (var e in json['items'] as List)
(e as Map<String, dynamic>)['name'] as String
];
#override
Map<String, dynamic> toJson(List<String> object) => {
'items': [
for (var item in object) {'name': item}
]
};
}
try this,
class Thing {
int id;
String base;
String tax;
String type;
int minimum;
Thing({
this.id,
this.base,
this.tax,
this.type,
this.minimum,
});
factory Thing.fromJson(Map<String, dynamic> json) {
return Thing(
id: json['_id']["oid"],
base: json['base'].toString(),
tax: json['tax'].toString(),
type: json['type'].toString(),
minimum: json['minimum'],
);
}
}
I have a class:
import 'package:google_maps_flutter/google_maps_flutter.dart';
class Place {
Place({
this.address,
this.coordinates,
});
final String address;
final LatLng coordinates;
}
LatLng is a class of google_maps_flutter. How can I make my Place class serializable using json_annotation and json_serializable?
Thank you very much!
You can explicitly specify which methods of LatLng should be used for serialization using JsonKey annotation:
#JsonSerializable()
class Place {
Place({
this.address,
this.coordinates,
});
final String address;
#JsonKey(fromJson: LatLng.fromJson, toJson: jsonEncode)
final LatLng coordinates;
}
Put this code in your model
to get the information from JSON response simply do that after your request
final place = placeFromJson(response.body);
to get address => = place.address
to get coordinates => place.coordinates.lng , place.coordinates.lat
=============================================
import 'dart:convert';
Place placeFromJson(String str) => Place.fromJson(json.decode(str));
String placeToJson(Place data) => json.encode(data.toJson());
class Place {
String address;
Coordinates coordinates;
Place({
this.address,
this.coordinates,
});
factory Place.fromJson(Map<String, dynamic> json) => Place(
address: json["address"],
coordinates: Coordinates.fromJson(json["coordinates"]),
);
Map<String, dynamic> toJson() => {
"address": address,
"coordinates": coordinates.toJson(),
};
}
class Coordinates {
String lat;
String lng;
Coordinates({
this.lat,
this.lng,
});
factory Coordinates.fromJson(Map<String, dynamic> json) => Coordinates(
lat: json["lat"],
lng: json["lng"],
);
Map<String, dynamic> toJson() => {
"lat": lat,
"lng": lng,
};
}
I didn't find a super easy solution for this. I ended up, writing a custom toJson/fromJson for the LatLng property. I've put it statically onto my model, but you could also create a global function, if you need to reuse it.
Take care, that in my example LatLng is nullable.
import 'dart:collection';
import 'dart:convert';
import 'package:latlong2/latlong.dart';
part 'challenge_model.g.dart';
#JsonSerializable()
class ChallengeModel with _$ChallengeModel {
ChallengeModel({
required this.startPosition,
});
#override
#JsonKey(fromJson: latLngFromJson, toJson: latLngToJson)
final LatLng? startPosition;
/// Create [ChallengeModel] from a json representation
static fromJson(Map<String, dynamic> json) => _$ChallengeModelFromJson(json);
/// Json representation
#override
Map<String, dynamic> toJson() => _$ChallengeModelToJson(this);
static String latLngToJson(LatLng? latLng) =>
jsonEncode(latLng != null ? {'latitude': latLng.latitude, 'longitude': latLng.longitude} : null);
static LatLng? latLngFromJson(String jsonString) {
final LinkedHashMap<String, dynamic>? jsonMap = jsonDecode(jsonString);
return jsonMap != null ? LatLng(jsonMap['latitude'], jsonMap['longitude']) : null;
}
}