How to get value from Json List - flutter - json

I have this code where i get json data into a list in flutter but i don't really know how to get the particular data i want like the value
main.dart
Future<String> loadDataFromJson() async {
return await rootBundle.loadString("assets/categories.json");
}
Future loadData() async {
String jString = await loadDataFromJson();
final jRes = json.decode(jString) as List;
List<Category> datas = jRes.map((e) => Category.fromJson(e)).toList();
print(datas);
}
#override
void initState() {
super.initState();
loadData();
}
Here I printed the data and it gave me this I/flutter ( 6111): [Instance of 'Category', Instance of 'Category', Instance of 'Category', Instance of 'Category']
Models
class Category {
final String catId;
final String catName;
Category({this.catId, this.catName});
factory Category.fromJson(Map<String, dynamic> json) {
return Category(catId: json['cat_id'], catName: json['category']);
}
}
my json is something like this but there are multiple
{
"category": "Design & Creativity",
"cat_id": "1",
"cat_suncategory": [
{
"sub_name": "Ads",
"sub_image": "https://images.unsplash.com/photo-1589838017489-9198a27bd040?ixid=MXwxMjA3fDB8MHxzZWFyY2h8Mnx8YWR2ZXJ0aXNlbWVudHxlbnwwfHwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60"
}
]
}
So please how do i get the value i want

I cannot understand your problem but this may help you;
If your json values in 'jRes', you can do
String myCategory = jRes["category"];
String subName = jRes["cat_suncategory"][0]["sub_name"];
String subImage = jRes["cat_suncategory"][0]["sub_image"];
Because of using '[0]' is; the 'cat_suncategory' is an array and you should take first element of it, it means [0].

Related

Can anyone explain the function of .map()

I was trying to make a Covid Tracking application using flutter, and I came across this function getCountrySummary( ),
import 'package:covid_tracker/models/country_summary.dart';
import 'package:covid_tracker/models/global_summary.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class CovidService {
Future<GlobalSummaryModel> getGlobalSummary() async {
final data =
await http.get(Uri.parse('https://api.covid19api.com/summary'));
if (data.statusCode != 200) {
throw Exception();
}
GlobalSummaryModel summary =
GlobalSummaryModel.fromJson(json.decode(data.body));
return summary;
}
Future<List<CountrySummaryModel>> getCountrySummary(String slug) async {
String url = "https://api.covid19api.com/total/dayone/country/$slug";
final data = await http.get(Uri.parse(url));
if (data.statusCode != 200) {
throw Exception();
}
List<CountrySummaryModel> summaryList = (json.decode(data.body) as List)
.map((item) => CountrySummaryModel.fromJson(item))
.toList();
return summaryList;
}
}
So I know what the function getCountrySummary() is trying to do, but I don't understand what statement
List<CountrySummaryModel> summaryList = (json.decode(data.body) as List).map((item) => CountrySummaryModel.fromJson(item)).toList();
is trying to do, and CountrySummaryModel is an object.
class CountrySummaryModel {
final String country;
final int confirmed;
final int death;
final int recovered;
final int active;
final DateTime date;
CountrySummaryModel(this.country, this.active, this.confirmed, this.date,
this.death, this.recovered);
factory CountrySummaryModel.fromJson(Map<String, dynamic> json) {
return CountrySummaryModel(
json["country"],
json["active"],
json["confirmed"],
DateTime.parse(json["date"]),
json["death"],
json["recovered"],
);
}
}
When you call Map on a list, it means you want to reach each item in it, in your case you call map on your list to parse each item in it and at then call toList() to make a list of this items.
If I understand your code correctly:
First, you convert data to List.
Then, use CountrySummaryModel.fromJson() and .toList() to convert it to List<CountrySummaryModel>.

Encode Map with Enum to JSON

I want to encode my Map to a json. It looks like this:
Map<MyEnum, int> map = {type: limit};
Where MyEnum is an enum. A simple json.encode(map) won't work as it does not know how to serialize the enum class I guess.
The error message is:
Unhandled Exception: Converting object to an encodable object failed: _LinkedHashMap len:1
How can I manage to serialize this map to a json?
you can use describeEnum method inside foundation.dart
This is really not a solution I would recommend but I ended up doing it mostly for "fun". I don't guarantee anything about the solution besides the fact that it is horrible. :)
The problem is that enum is not defined as a valid type for Json so the whole concept does give us some problems. One solution is to translate enum values into String with the name of the enum first, and then the name of value like MyFirstEnum.first1. This representation is what Dart gives you if calling toString() on a enum value.
This is fine but for safety we could also add a magic string in the beginning like DART_ENUM:MyFirstEnum.first1 so it is easier to recognize between other strings which could have the same name as the enum value without being an enum.
Next is type safety. In Json, we know that all maps has String as the type of keys. By making our own representation of enum and allowing it to also be keys, we cannot expect a decoder to return e.g. Map<String, dynamic> but must return Map<dynamic, dynamic>.
With that said, here is my attempt to build a Json decoder and encoder which handles enum values. It also works for enum keys in maps:
import 'dart:convert';
class JsonConverterWithEnumSupport {
final String magicString;
final Set<Object> allEnumValues = {};
final Map<String, Object> enumStringToEnumValue = {};
JsonConverterWithEnumSupport(List<List<Object>>? enumsValues,
{this.magicString = "DART_ENUM:"}) {
enumsValues?.forEach(addEnumValues);
}
void addEnumValues(List<Object> enumValues) {
for (final enumValue in enumValues) {
enumStringToEnumValue[enumValue.toString()] = enumValue;
allEnumValues.add(enumValue);
}
}
String _addMagic(dynamic enumValue) => '$magicString$enumValue';
String _removeMagic(String string) => string.substring(magicString.length);
String encode(Object? value) =>
json.encode(value, toEncodable: (dynamic object) {
if (object is Map) {
return object.map<dynamic, dynamic>((dynamic key, dynamic value) =>
MapEntry<dynamic, dynamic>(
allEnumValues.contains(key) ? _addMagic(key) : key,
allEnumValues.contains(value) ? _addMagic(value) : value));
}
if (object is List) {
return object.map<dynamic>(
(dynamic e) => allEnumValues.contains(e) ? _addMagic(e) : e);
}
if (allEnumValues.contains(object)) {
return _addMagic(object);
}
return object;
});
dynamic decode(String source) => json.decode(source, reviver: (key, value) {
if (value is String && value.startsWith(magicString)) {
return enumStringToEnumValue[_removeMagic(value)];
}
if (value is Map) {
return value.map<dynamic, dynamic>((dynamic key, dynamic value) =>
MapEntry<dynamic, dynamic>(
(key is String) && key.startsWith(magicString)
? enumStringToEnumValue[_removeMagic(key)]
: key,
value));
}
return value;
});
}
enum MyFirstEnum { first1, first2 }
enum MySecondEnum { second1, second2 }
void main() {
final converter =
JsonConverterWithEnumSupport([MyFirstEnum.values, MySecondEnum.values]);
final jsonString = converter.encode({
MyFirstEnum.first1: [MySecondEnum.second2, MySecondEnum.second1],
'test': {MyFirstEnum.first2: 5}
});
print(jsonString);
// {"DART_ENUM:MyFirstEnum.first1":["DART_ENUM:MySecondEnum.second2","DART_ENUM:MySecondEnum.second1"],"test":{"DART_ENUM:MyFirstEnum.first2":5}}
print(converter.decode(jsonString));
// {MyFirstEnum.first1: [MySecondEnum.second2, MySecondEnum.second1], test: {MyFirstEnum.first2: 5}}
}
You will need to feed into JsonConverterWithEnumSupport all the possible enum values there is possible (see the main method in the bottom for example).
If you don't want the magic string to be appended on each enum you can just create JsonConverterWithEnumSupport with: magicString: '' as parameter.
You could create an extension on your enum to convert it to a String then convert your map to a Map<String, int> so it will be encoded correctly:
import 'dart:convert';
enum MyEnum { type }
extension MyEnumModifier on MyEnum {
String get string => this.toString().split('.').last;
}
void main() {
Map<MyEnum, int> map = {MyEnum.type: 10};
Map<String, int> newMap = {};
map.forEach((key, value) =>
newMap[key.string] = value);
final json = jsonEncode(newMap);
print(json);
}
Output
{"type":10}
you will need a function to convert string to enum:
T enumFromString<T>(List<T> values, String value) {
return values.firstWhere((v) => v.toString().split('.')[1] == value, orElse: () => null);
}
your enum is
enum MyEnum {type, destype};
suppose it is used as map inside a class to serialize and deserialize:
class MyClass {
Map<MyEnum, int> myProperty = {type: 1};
// serialize
Map<String, dynamic> toJson() {
return {
'myProperty': myProperty.map((key, value) => MapEntry(key.name, value)),
}
// deserialize
MyClass.fromJson(Map<String, dynamic> json) {
myProperty=
json['myProperty'].map((k, v) => MapEntry(enumFromString<MyEnum>(MyEnum.values, k), v)).cast<MyEnum, int>();
}
}
Firstly define enum and value extension
enum OrderState { pending, filled, triggered, cancelled }
extension OrderStateExt on OrderState {
String get value {
switch (this) {
case OrderState.pending:
return "PENDING";
case OrderState.filled:
return "FILLED";
case OrderState.triggered:
return "TRIGGERED";
case OrderState.cancelled:
return "CANCELLED";
}
}
}
model class
class OrderRequest {
OrderState state;
OrderRequest({required this.state});
Map<String, dynamic> toMap() {
return {
'state': state.value,
};
}
}
String toJson() => json.encode(toMap());
factory OrderRequest.fromMap(Map<String, dynamic> map) {
return OrderRequest (
state: OrderState.values
.where((e) => e.value == map['state']).first
);
}
factory OrderRequest.fromJson(String source) =>
OrderRequest.fromMap(json.decode(source));

dart/Flutter: Problems with Decodeing json with utf8 decode

I try to load a json-file to put it in a filterable/searchable Listview (search for a diagnosis with a symptom). I'm new to in programming so probably there is a better / simpler way to do this but i would like to do it this way, so it doesnt get more complicated.
I get this error if i try to use utf8.decode:
"The argument type 'String' can't be assigned to the parameter type 'List'."
This is what i tried:
class Services {
static Future<List<User>> getUsers() async {
final response = await rootBundle.loadString('assets/diff.json');
List<User> list = parseUsers(response);
return list;
}
static List<User> parseUsers(String responseBody) {
final parsed = json.decode(utf8.decode(responseBody)).cast<Map<String, dynamic>>();
return parsed.map<User>((json) => User.fromJson(json)).toList();
}
}
the User Class:
class User {
String symptom;
String diagnosis;
User(this.symptom, this.diagnosis);
User.fromJson(Map<String, dynamic> json){
symptom = json['symptom'];
diagnosis = json['diagnosis'];
}
}
extract of the json file:
[
{"symptom":"Kopfschmerz","diagnosis":"Migräne, Spannungskopfschmerz"}
,
{"symptom":"Bauchschmerz","diagnosis":"Apendizitis, Infektion"}
]
Is there a simple way to make this work? Thanks!
With dynamic json.decode(String) the returned object can have a real type of:
List<dynamic>
Map<String, dynamic>
But also when the type is List<dynamic>, decode has parsed also the items in the List, so in your case (since your json has structure [{"" : ""}]) you just need to cast the (reified) List's type parameter with the cast() method.
static List<User> parseUsers(String responseBody) {
//final parsed = json.decode(utf8.decode(responseBody)).cast<Map<String, dynamic>>();
final parsed = (json.decode(responseBody) as List<dynamic>).cast<Map<String, dynamic>>();
return parsed.map<User>((json) => User.fromJson(json)).toList();
}

How does built_value deserialize json array to object array?

In built_value's official example show how to setup an array member of an object:
abstract class Collections implements Built<Collections, CollectionsBuilder> {
static Serializer<Collections> get serializer => _$collectionsSerializer;
BuiltList<int> get list;
BuiltMap<String, int> get map;
BuiltListMultimap<int, bool> get listMultimap;
factory Collections([updates(CollectionsBuilder b)]) = _$Collections;
Collections._();
}
it just demonstrate how to deserialize a map but not array, the array just a key/member not the data itself.
but in may case, my http response is an array itself, not play as a member of the response.
my model:
abstract class Post implements Built<Post, PostBuilder> {
static Serializer<Post> get serializer => _$postSerializer;
int get userId;
String get title;
factory Post([updates(PostBuilder b)]) = _$Post;
Post._();
}
my request is:
static Future<List<Post>> getPosts() async {
final response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
return serializers.deserialize(
json.decode(response.body),
specifiedType: FullType(Post)
);
} else {
throw Exception('Failed to load post');
}
}
response.body:
[
{'userId': 1, 'title': 'title1'},
{'userId': 2, 'title': 'title2'}
]
I looked up every tutorial or network discussion, no one mentions this scenario, or should I can only iterate the response.body and deserialize to object one by one? for example:
static Future<List<Post>> getPosts() async {
final response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
return data.map((m){
return serializers.deserializeWith(Post.serializer, m);
}).toList();
} else {
throw Exception('Failed to load post');
}
}
according to #David Morgan 's reply, built_value is not support deserialize a list yet.
Currently there’s no equivalent to deserializeWith for a list. You’ll need to deserialize one by one.

Angular4 - why does custom toJSON() only get called on new objects?

I have this code. Notice that the serialization is simply renaming the template_items property to template_items_attributes:
export class Template {
constructor(
) {}
public id: string
public account_id: string
public name: string
public title: string
public info: string
public template_items: Array<TemplateItem>
toJSON(): ITemplateSerialized {
return {
id: this.id,
account_id: this.account_id,
name: this.name,
title: this.title,
info: this.info,
template_items_attributes: this.template_items
}
}
}
export interface ITemplateSerialized {
id: string,
account_id: string,
name: string,
title: string,
info: string,
template_items_attributes: Array<TemplateItem>
}
Creating an object locally works fine and stringify calls the toJSON() method.
However, once I send that object to the API:
private newTemplate(name: string): Template {
let template = new Template();
template.name = name;
template.account_id = this._userService.user.account_id;
// next 5 lines are for testing that toJSON() is called on new obj
let item = new TemplateItem();
item.content = "Test"
template.template_items.push(item);
let result = JSON.stringify(template);
console.log('ready', result); // SHOWS the property changes
return template;
}
postTemplate(name: string): Observable<any> {
return this._authService.post('templates', JSON.stringify(this.newTemplate(name)))
.map((response) => {
return response.json();
});
}
It is saved and returned, but from that point on when I stringify and save again it does NOT call toJSON().
patchTemplate(template: Template): Observable<any> {
console.log('patching', JSON.stringify(template)); // DOES NOT CHANGE!
return this._authService.patch('templates' + `/${template.id}`, JSON.stringify(template))
.map((response) => {
return response.json();
});
}
Why does toJSON() only work on new objects?
In fact, your question has nothing to do with Angular or Typescript, it's just some JavaScript and the logic of how serialization work and why do we serialize objects.
I send that object to the API, save and return it
When you return an "object" from an API, you're returning a string which you parse as a JSON serialized object. Then you get a plain JavaScript object, not an instance of your class.
Object prototype in JavaScript does not have toJSON method, and even if it had, it's not the method you've written inside the Template class, so it won't be called.
You don't even need a server call to replicate this, just do
const obj = JSON.parse(JSON.stringify(new Template()))
obj.toJSON // undefined
And you'll see that obj is not an instance of Template. It's simply an object which simply happens to have all the fields as your original object made as a Template instance, but it's not an instance of that class.