Parse JSON to convert into Model class - json

I'm not able to convert server response to model class. The following is my code.
void main() {
//JSON to parse
var strJson = """{
\"person\": [
{\"name\": \"Mahendra\", \"age\": 28},
{\"name\": \"Test\", \"age\": 25}
]
}""";
var data = json.decode(strJson);
print("json: $data");
var result = PersonResponse<Person>.fromJSON(data);
print("result: ${result.persons}");
}
Model Class
class Person {
String name;
int age;
Person.fromJSON(Map json) {
this.name = json["name"];
this.age = json["age"];
}
}
class PersonResponse<T> {
List<T> persons;
PersonResponse.fromJSON(Map json) {
this.persons = json["person"];
}
}
When I run this code I'm not able to convert server response to model class. Getting following error...
Unhandled Exception: type List<dynamic> is not a subtype of type List<Person>
Whats wrong with my code. Any suggestions?

try
// To parse this JSON data, do
//
// final person = personFromJson(jsonString);
import 'dart:convert';
Person personFromJson(String str) => Person.fromJson(json.decode(str));
String personToJson(Person data) => json.encode(data.toJson());
class Person {
List<PersonElement> person;
Person({
this.person,
});
factory Person.fromJson(Map<String, dynamic> json) => Person(
person: List<PersonElement>.from(json["person"].map((x) => PersonElement.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"person": List<dynamic>.from(person.map((x) => x.toJson())),
};
}
class PersonElement {
String name;
int age;
PersonElement({
this.name,
this.age,
});
factory PersonElement.fromJson(Map<String, dynamic> json) => PersonElement(
name: json["name"],
age: json["age"],
);
Map<String, dynamic> toJson() => {
"name": name,
"age": age,
};
}

I found 3 options:
You can either abandon usage of generics and replace List<T> persons; with List<dynamic> persons;. Your code will actually work with only this change.
You can abandon usage of generics and replace List<T> persons; with List<Person> and map it in fromJson.
class PersonResponse {
List<Person> persons;
PersonResponse.fromJSON(Map json) {
List<dynamic> list = json["person"];
persons = list.map((element) => Person.fromJSON(element)).toList();
}
}
Keep generics but restrict in to something serializable, to something like this:
class PersonResponse<T extends JsonSerializable> {
List<T> persons;
PersonResponse.fromJSON(Map json) {
List<dynamic> list = json["person"];
persons = list.map((element) => T.fromJSON(element)).toList();
}
}

Don't convert JSON into dart object manually. Because some time JSON response are very complex So it may happen you can write incorrect data type. So always use Online JSON to Dart Convertor. It is free to use. It will reduce the chance of error.

Related

How do I get the value from nested locally stored json-file in Flutter?

I'm trying to get the nested values from my locally stored json file with Flutter.
I can get the "outer" values, but I haven't been able to get the "inner" ones. I have googled and searched here, but I still can't make it work, so any help is much appreciated.
I put the code in a sandbox to make it easier to see.
https://codesandbox.io/s/loving-thunder-meklbc?file=/lib/main.dart
If you rather look here this is what some files look like:
json:
[{
"id":184423,
"created":"2022-11-18T09:32:56.000Z",
"raw_data":[
{"measurement_id":18,"index":0,"substance":655,"pressure":20,"temperature":30.03},
{"measurement_id":18,"index":1,"substance":648,"pressure":38,"temperature":30.03},
{"measurement_id":18,"index":2,"substance":636,"pressure":90,"temperature":30.02},
{"measurement_id":18,"index":3,"substance":623,"pressure":130,"temperature":30.05},
{"measurement_id":18,"index":4,"substance":598,"pressure":147,"temperature":29.99}
]
},
{
"id":184423,
"created":"2022-11-19T09:32:56.000Z",
"raw_data":[
{"measurement_id":19,"index":0,"substance":586,"pressure":160,"temperature":30.05},
{"measurement_id":19,"index":1,"substance":564,"pressure":170,"temperature":29.99},
{"measurement_id":19,"index":2,"substance":553,"pressure":173,"temperature":30},
{"measurement_id":19,"index":3,"substance":544,"pressure":162,"temperature":30.02},
{"measurement_id":19,"index":4,"substance":538,"pressure":164,"temperature":30.01}
]
}
]
handler:
import 'dart:convert';
import 'package:flutter/services.dart' as rootbundle;
import '../model/usermodel.dart';
Future<List<UserModel>> readJsonData() async {
final jsondata = await rootbundle.rootBundle.loadString('/userdata.json');
final list = json.decode(jsondata) as List<dynamic>;
//print(list);
return list.map((e) => UserModel.fromJson(e)).toList();
}
model:
// ignore_for_file: non_constant_identifier_names
class UserModel {
late int? id, measurementId, index, substance, pressure;
late double? temperature;
UserModel(
this.id,
this.measurementId,
this.index,
this.substance,
this.pressure,
this.temperature,
);
UserModel.fromJson(Map<String, dynamic> json) {
id = json["id"];
measurementId = json['measurement_id'];
index = json['index'];
substance = json['substance'];
pressure = json['pressure'];
temperature = json['temperature'];
}
}
class UserModel {
UserModel(this.id, this.raw_data);
/// Creates a UserModel from Json map
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
json['id'] as int?,
(json['raw_data'] as List<dynamic>?)
?.map((e) => Data.fromJson(e as Map<String, dynamic>))
.toList(),
);
final int? id;
final List<Data>? raw_data;
}
//Data
class Data {
Data(
this.measurement_id,
this.index,
this.substance,
this.pressure,
this.temperature,
);
final int? measurement_id;
final int? index;
final int? substance;
final int? pressure;
final double? temperature;
/// Creates a Data from Json map
factory Data.fromJson(Map<String, dynamic> json) => Data(
json['measurement_id'] as int?,
json['index'] as int?,
json['substance'] as int?,
json['pressure'] as int?,
(json['temperature'] as num?)?.toDouble(),
);
}
List<UserModel> models = [];
for (var item in list) {
models.addAll(item.map((e) => UserModel.fromJson(e['id'], e['raw_data'])));
}
return models;
UserModel.fromJson(int id, Map<String, dynamic> json) {
this.id = id; // parse json (raw_data)
}

How to decode nested JSON List of Objects in Dart/Flutter

I'm trying to figure out how I can decode the following JSON in Flutter.
https://covid.ourworldindata.org/data/owid-covid-data.json
I tried the following structure, but it doesn't work.
#JsonSerializable()
class StatisticsResponse {
Map<String, Country> data;
//List<Country> data;
StatisticsResponse({this.data});
factory StatisticsResponse.fromJson(Map<String, dynamic> json) =>
_$StatisticsResponseFromJson(json);
}
#JsonSerializable()
class Country {
String continent;
String location;
int population;
//Map<String, Data> data;
List<Data> data;
Country({this.continent, this.location, this.population, this.data
});
factory Country.fromJson(Map<String, dynamic> json) =>
_$CountryFromJson(json);
}
Use Map to convert dynamic to String. Directly assigning List to List will throw an error. You have create getters yourself.
Consider this example:
(jsonDecode(response.body)["data"] as List).map((e) => e as Map<String, dynamic>)?.toList();
Now assign to an instance of the custom class you have made.

Dart: converting _InternalLinkedHashMap<dynamic, dynamic> in constructor fails

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

How to parse json array into list in dart?

As for the script below, I have two different json for single record and multiple record as a list. I can parse the single data using the Person class. For the json with a list, I'm using ther PersonList class but I'm getting an error because the json key result is not needed anymore. Is there a way to parse the list without changing the Person class? Or should I not use the PersonList class and just create a List<Person>?
I saw this example but its only working if the json is a whole list like this
var jsonResponse = convert.jsonDecode(rawJsonMulti) as List;
return jsonResponse.map((p) => Person.fromJson(p).toList();
Can you show me how to use the above script using my json. Thanks.
import 'dart:convert';
void main() {
String rawJsonMulti = '{"result": [{"name":"Mary","age":30},{"name":"John","age":25}]}';
String rawJsonSingle = '{"result": {"name":"Mary","age":30}}';
// Working for non list json
// final result = json.decode(rawJsonSingle);
// var obj = Person.fromJson(result);
// print(obj.name);
final result = json.decode(rawJsonMulti);
var obj = PersonList.fromJson(result);
print(obj.listOfPerson[0].name);
}
class PersonList {
final List<Person> listOfPerson;
PersonList({this.listOfPerson});
factory PersonList.fromJson(Map<String, dynamic> json) {
var personFromJson = json['result'] as List;
List<Person> lst =
personFromJson.map((i) => Person.fromJson(i)).toList();
return PersonList(listOfPerson: lst);
}
}
class Person {
String name;
int age;
//will only work on result is not a list
Person({this.name, this.age});
factory Person.fromJson(Map<String, dynamic> json) {
return Person(name: json['result']['name'],
age: json['result']['age']);
}
// This will work on json with list but not in single
// factory Person.fromJson(Map<String, dynamic> json) {
// return Person(name: json['name'],
// age: json['age']);
// }
}
If you convert the dynamic to a List<dynamic> you can map each element in the jsonDecoded list.
Example:
import 'dart:convert';
final decodedJson = jsonDecode(rawJsonMulti) as List<dynamic>;
final personList = decodedJson
.map((e) => Person.fromJson(
e as Map<String, dynamic>))
.toList();
return PersonList(listOfPerson: personList);
```
Try take a look at this solution where I have fixed your code:
import 'dart:convert';
void main() {
const rawJsonMulti =
'{"result": [{"name":"Mary","age":30},{"name":"John","age":25}]}';
const rawJsonSingle = '{"result": {"name":"Mary","age":30}}';
final resultMulti = json.decode(rawJsonMulti) as Map<String, dynamic>;
final personListMulti = PersonList.fromJson(resultMulti);
print(personListMulti.listOfPerson[0]); // Mary (age: 30)
print(personListMulti.listOfPerson[1]); // John (age: 25)
final resultSingle = json.decode(rawJsonSingle) as Map<String, dynamic>;
final personListSingle = PersonList.fromJson(resultMulti);
print(personListSingle.listOfPerson[0]); // Mary (age: 30)
}
class PersonList {
final List<Person> listOfPerson;
PersonList({this.listOfPerson});
factory PersonList.fromJson(Map<String, dynamic> json) {
if (json['result'] is List) {
final personsFromJson = json['result'] as List;
return PersonList(listOfPerson: [
...personsFromJson
.cast<Map<String, Object>>()
.map((i) => Person.fromJson(i))
]);
} else {
final personFromJson = json['result'] as Map<String, Object>;
return PersonList(listOfPerson: [Person.fromJson(personFromJson)]);
}
}
}
class Person {
String name;
int age;
Person({this.name, this.age});
factory Person.fromJson(Map<String, dynamic> json) {
return Person(name: json['name'] as String, age: json['age'] as int);
}
#override
String toString() => '$name (age: $age)';
}

Access nested objects in json using json_serializable in Dart

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'],
);
}
}