Parsing issue with Dart - [no build value ^ serializable ^ ] - json

Aye Aye good people,
[edited:
running this in dartpad
import 'dart:convert';
void main() {
const String _json = '{"myListInt": [1]}';
final Map<String, dynamic> _map = jsonDecode(_json);
final List<int> _list = _map['myListInt'] as List<int>;
_list.forEach((i) {
String _s = i.toString();
print(_s);
});
}
returns
Uncaught exception:
CastError: Instance of 'JSArray': type 'JSArray' is not a subtype of type
'List<int>'
in case I use
final List<int> _list = List<int>.from(_map['myListInt'] as List<int>);
or
List<int>.generate(_map['myListInt'].length, (i)=>_map['myListInt'][i] as int);
returns
Uncaught exception:
Cannot read property 'length' of undefined
]
what am I doing wrong?
Thank you in advance
Francesco

Instead of this line
myListInt: List<int>.from(_map['myListInt'] as List<int>),
you can use
myListInt: List<int>.generate(_map['myListInt'].length, (i)=>_map['myListInt'][i] as int
Basically instead of casting the whole list, you have to cast each element one by one.

ok, using ""as Iterable"" works,
import 'dart:convert';
void main() {
const String _json = '{"myListInt": [1]}';
final Map<String, dynamic> _map = jsonDecode(_json);
final List<int> _list= List<int>.from(_map['myListInt'] as Iterable);
_list.forEach((i) {
String _s = i.toString();
print(_s);
});
}

Related

jsonEncode throwing exceptions encoding a simple class

Friends
I have a simple Dart class that cannot be encoded into JSON.
The output of the following code prints out to the console
flutter: Converting object to an encodable object failed: Instance of 'TestJsonConversion'
class TestJsonConversion {
String testString = "123245abcde";
int testIneger = 1234;
}
void main() {
var testJsonConversion = TestJsonConversion();
try {
var testString = jsonEncode(testJsonConversion);
// ignore: avoid_print
print(testString);
}catch(e){
// ignore: avoid_print
print(e.toString());
}
runApp(const MyApp());
}
This is the default application generated by Visual Studio with just these lines added.
You cannot encode an instance of a user class with the built-in jsonEncode. These are things you can encode by default: "a number, boolean, string, null, list or a map with string keys". For this class to encode, you'd have to define a .toJson method on it, and I don't see one there.
The class has no constructors and tojson . Try this
class TestJsonConversion {
final String testString;
final int testInteger;
TestJsonConversion(this.testString, this.testInteger);
TestJsonConversion.fromJson(Map<String, dynamic> json)
: testString = json['trstString'],
testInteger = json['testInteger'];
Map<String, dynamic> toJson() => {
'testString': testString,
'testInteger': testInteger,
};
}
And when you create an instance
var testJsonConversion = TestJsonConversion(testString: 'abc', testInteger: 123);
print(json.encode(testJsonConversion.toJson());

Non nullable instance field must be initalised

im new to Dart-flutter.
i have watching a tutorial video from udemy course writing in dart pad
so i also wrote in dartpad. but it showing error in it.
this is the code wrote in udemy..
import 'dart:convert';
void main(){
var rawJson = '{"url": "https://helo.com","id": 2}';
var parsedJson = json.decode(rawJson);
var imageModel = new ImageModel.fromJson(parsedJson);
print(imageModel.url);
}
class ImageModel{
int id;
String url;
ImageModel.fromJson(parsedJson) {
id = parsedJson['id'];
url = parsedJson['url'];
}
ImageModel(this.id, this.url);
}
in that video it runs, but for me it shows error as
Error compiling to JavaScript:
Info: Compiling with sound null safety
Warning: Interpreting this as package URI, 'package:dartpad_sample/main.dart'.
lib/main.dart:15:3:
Error: This constructor should initialize field 'id' because its type 'int' doesn't allow null.
ImageModel.fromJson(parsedJson) {
^
lib/main.dart:12:7:
Info: 'id' is defined here.
int id;
^^
lib/main.dart:15:3:
Error: This constructor should initialize field 'url' because its type 'String' doesn't allow null.
ImageModel.fromJson(parsedJson) {
^
lib/main.dart:13:10:
Info: 'url' is defined here.
String url;
^^^
Error: Compilation failed.
i have no idea what the problem is..
can you guys help me to troubleshoot the error
Since you're compiling with null safety, you may change to this:
int? id;
String? url;
For further info about null safety, please refer to this link https://dart.dev/null-safety
Try using null safe code:
eg:
int? id;
String? url;
You will try like this
import 'dart:convert';
class ImageModel{
final int id;
final String url;
ImageModel({required this.id, required this.url});
factory ImageModel.fromJson(Map<String, dynamic> parsedJson) {
return ImageModel(
id: parsedJson['id'] as int,
url: parsedJson['url'] as String,
);
}
}
void main() {
var rawJson = '{"url": "https://helo.com","id": 2}';
var parsedJson = json.decode(rawJson);
print(parsedJson['url']);
print(parsedJson['id']);
}
You are probably watching tutorial that was recorded before null-safety introduction in Dart (null-safety is a Dart feature that helps prevent NullRefference exceptions in runtime).
You can either go with nullable values (like #Sowat Kheang suggested in another answer) for id and url in ImageModel class (change String to String? and int to int?). Then you will be fine, but it defeats the purpose of null-safety.
Or you can change your code so it will handle null values in proper way. Code will look something like this:
import 'dart:convert';
void main(){
var rawJson = '{"url": "https://helo.com","id": 2}';
var parsedJson = json.decode(rawJson);
var imageModel = ImageModel.fromJson(parsedJson);
print(imageModel.url);
}
class ImageModel{
// Make your fields 'final' if you don't plan to change them later
// this will make your life easier and is a good practice.
final int id;
final String url;
// we add curly braces to make arguments named
// and add 'required' keyword to indicate that constructor can't be called
// without actually passing arguments into it
ImageModel({required this.id, required this.url});
// here we use factory constructor that will call our default constructor
// it's also a good practice to specify type of a variable if you know it (Map<String, dynamic> in this case)
factory ImageModel.fromJson(Map<String, dynamic> parsedJson) {
// since we don't know for sure if 'parsedJson' has key 'id'
// we would add '??' — null checking operator.
// So if parsedJson['id'] is null, value -1 will be passed
// to the constructor. Same with 'parsedJson['url']'.
return ImageModel(
id: parsedJson['id'] ?? -1,
url: parsedJson['url'] ?? '');
}
}
var rawJson = '{"url": "https://helo.com","id": 2}';
Actually it is a Sting Json and Encoding is not perform properly So follow the step..
Map data={"url": "https://helo.com","id": 2};
Encode that data. json.encode(data);
Now the Encoding is done then you can send the data any server if you have..
When you get back data then... json.decode(rawData)
Then parsing work properly

Flutter Persistence: how to jsonDecode a List<dynamic> to List<ClassType>?

I have a Todo-List app with Task class.
I want to serialize a Task List with jsonEncode and persist them onto a file in Docs dir.
after that, I want to be able to re-serialize the same list and convert them into my native List datatype (from List<String, dynamic> that I get from jsonDecode). Whats the best way to do it?
Currently, I tried:
void reSerializeTaskList() async {
final directory = await getApplicationDocumentsDirectory();
File f = File('${directory.path}/new.txt');
String fileContent = await f.readAsString();
List<dynamic> jsonList = jsonDecode(fileContent).cast<Task>(); // does not work
print("JSONSTRING: ${jsonList.runtimeType}");
print("$jsonList");
}
I/flutter (29177): JSONSTRING: CastList<dynamic, Task>
E/flutter (29177): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Task' in type cast
my workaround is to iterate through all array elements and build a Task type out of the values with "fromJson" method inside my Task class:
void reSerializeTaskList() async {
final directory = await getApplicationDocumentsDirectory();
File f = File('${directory.path}/new.txt');
String fileContent = await f.readAsString();
List<dynamic> jsonList = jsonDecode(fileContent);
List<Task> taskList = [];
for (var t in jsonList) {
print("T: $t and ${t.runtimeType}");
Task task = new Task();
taskList.add(task.fromJson(t));
}
print("JSONSTRING: ${jsonList.runtimeType}");
print("$jsonList");
print("$taskList");
print("$taskList.runtimeType");
}
my Task class:
import 'dart:io';
class Task {
String name;
bool isDone;
Task({this.name, this.isDone = false});
void toggleDone() {
isDone = !isDone;
}
#override
String toString() {
// TODO: implement toString
return "${this.name} is done: $isDone";
}
Map<String, dynamic> toJson() {
return {
"name": this.name,
"isDone": this.isDone,
};
}
Task fromJson(Map<String, dynamic> json) {
this.name = json['name'];
this.isDone = json['isDone'];
return this;
}
}
But is there maybe another (better) approach? This looks quite patchy to me...
Just to give you a little example, this is how I do it
final jsonResponse = json.decode(jsonString);
final List<Customer> customers = jsonResponse.map<Customer>((jR) => Customer.fromJson(jR)).toList();
and fromJson in Customer class looks like this
factory Customer.fromJson(Map<String, dynamic> json) => Customer(
id: json["id"] == null ? null : json["id"],
changeDate: json["changeDate"] == null ? null : DateTime.parse(json["changeDate"]),
name: json["name"] == null ? null : json["name"],
);

Mapping a model from JSON give me a error when map int value

Hi im trying to get values from JSOn but when I map a value that is a INT then flutter show that error and no return data :
flutter: type 'String' is not a subtype of type 'int'
This is my model :
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
class Kits {
final String country;
final String franchise;
final String type;
String brand;
String procedure;
String setdesc;
int traymaterialid;
String traydesc;
int referencia;
String descart;
int cantidad;
int homologado;
int bajopresu;
DateTime insdate;
Kits(this.country,this.franchise,this.type, this.brand, this.procedure,this.traydesc, this.traymaterialid);
factory Kits.fromJson(Map<String, dynamic> json) {
return Kits(json['COUNTRY'], json['FRANCHISE'], json['TYPE'], json['BRAND'], json['PROCEDURE'], json['TRAYDESCRIPTION'], json['TRAYMATERIALID'] /* <-- INT*/ );
}
}
class KitsData {
static const url = 'http://app-smith.com/en/kits_API.php?email=miquel#winfor.es&macid=888&passwd=Wcz95f4UGkax5G';
final JsonDecoder _decoder = new JsonDecoder();
Future<List<Kits>> fetch(){
return http.get(url).then((http.Response response){
final String jsonBody = response.body;
//final statusCode = response.statusCode;
/*if (statusCode < 200 || statusCode >= 300 || jsonBody == null){
return List();
}*/
final kitsContainer = _decoder.convert(jsonBody);
final List kitsItems = kitsContainer['kits'];
return kitsItems.map( (kitsRaw) => new Kits.fromJson(kitsRaw) ).toList();
});
}
}
class FetchDataException implements Exception {
String _message;
FetchDataException(this._message);
String toString() {
return "Exception: $_message";
}
}
I don't understand why it's happen because fromJson is only getting the value that I pass the key then in my list I convert to String the value .
Fragment of JSON:
{"kits": [{"COUNTRY":"FR","FRANCHISE":"KNEE","TYPE":"LEGION","BRAND":"REVISION","PROCEDURE":"LEGION REVISION","SETDESCRIPTION":"LEGION REVISION - INSTRUMENTS","TRAYMATERIALID":"551820141LRC","TRAYDESCRIPTION":"INSTR LEGION REVISION","REFERENCIA":"71431722","DESCRIPCIONARTICULO":"LGN SCW LWDG TRL S3 5D X 10P","CANTIDAD":"1","HOMOLOGADO":"","BAJOPRESUPUESTO":"","INS_DATE":"2018-08-23 18:57:04"}
All numeric values are inside quotes, so they are not transferred as int but as String. The error message is correct. If you want them as String either ensure the values are not quoted, if you don't control that use int.parse() to convert them from String to int

Flutter - How to parsed nested json to a class with generics?

I'm wondering how can I parse a nested json to a class with generic types. My intention is to wrap responses from the backend (like loginRespose that contains a token) with a code and a message
I have
class BaseResponse<T>{
int code;
String message;
T responseObject;
BaseResponse.fromJson(Map<String, dynamic> parsedJson)
: code = parsedJson['Code'],
message = parsedJson['Message'],
responseObject = T.fromJson(parsedJson['ResponseObject']); //This is what I'd like to do
}
Obviously the last line throws an error because T doesn't has a named constructor "fromJson".
I tried adding some restrictions to the Type but I didn't find any solutions. Do you have any idea on how to pull this off?
You can't do such thing, at least not in flutter. As dart:mirror is disabled and there's no interface for classes constructors.
You'll have to take a different route.
I'll recommend using POO instead. You would here give up on deserializing responseObject from your BaseResponse. And then have subclass of BaseResponse handles this deserialization
Typically you'd have one subclass per type:
class IntResponse extends BaseResponse<int> {
IntResponse.fromJson(Map<String, dynamic> json) : super._fromJson(json) {
this.responseObject = int.parse(json["Hello"]);
}
}
You can then hide this mess away by adding a custom factory constructor on BaseResponse to make it more convenient to use.
class BaseResponse<T> {
int code;
String message;
T responseObject;
BaseResponse._fromJson(Map<String, dynamic> parsedJson)
: code = parsedJson['Code'],
message = parsedJson['Message'];
factory BaseResponse.fromJson(Map<String, dynamic> json) {
if (T == int) {
return IntResponse.fromJson(json) as BaseResponse<T>;
}
throw UnimplementedError();
}
}
Then either instantiate the wanted type directly, or use the factory constructor :
final BaseResponse foo = BaseResponse.fromJson<int>({"Hello": "42", "Code": 42, "Message": "World"});
You can achieve this with the built_value package (you'll also need built_value_generator and build_runner). Your class will look something like this:
part 'base_response.g.dart';
abstract class BaseResponse<T> implements Built<BaseResponse<T>, BaseResponseBuilder<T>> {
int code;
String message;
T responseObject;
factory BaseResponse([updates(BaseResponseBuilder<T> b)]) = _$BaseResponse<T>;
static Serializer<BaseResponse> get serializer => _$baseResponseSerializer;
}
You will have to run flutter packages pub run build_runner build to make the generated file. Then you use it like this:
BaseResponse baseResponse = serializers.deserialize(
json.decode(response.body),
specifiedType: const FullType(BaseResponse, const [FullType(ConcreteTypeGoesHere)])
);
There's just one more bit of boilerplate you have to take care of. You need another file called serializers.dart. You need to manually add all the classes you want to deserialize here, and also an addBuilderFactory function for each class that takes a type parameter - and for each concrete type you want to use.
part 'serializers.g.dart';
#SerializersFor(const [
BaseResponse,
ConcreteTypeGoesHere,
])
final Serializers serializers = (_$serializers.toBuilder()
..addBuilderFactory(
FullType(BaseResponse, const [const FullType(ConcreteTypeGoesHere)]),
() => new BaseResponseBuilder<ConcreteTypeGoesHere>()
)
..addPlugin(StandardJsonPlugin()))
.build();
Then re-run flutter packages pub run build_runner build
Makes me wish for Gson... :S
Here is my approach:
class Wrapper<T, K> {
bool? isSuccess;
T? data;
Wrapper({
this.isSuccess,
this.data,
});
factory Wrapper.fromJson(Map<String, dynamic> json) => _$WrapperFromJson(json);
Map<String, dynamic> toJson() => _$WrapperToJson(this);
}
Wrapper<T, K> _$WrapperFromJson<T, K>(Map<String, dynamic> json) {
return Wrapper<T, K>(
isSuccess: json['isSuccess'] as bool?,
data: json['data'] == null ? null : Generic.fromJson<T, K>(json['data']),
);
}
class Generic {
/// If T is a List, K is the subtype of the list.
static T fromJson<T, K>(dynamic json) {
if (json is Iterable) {
return _fromJsonList<K>(json) as T;
} else if (T == LoginDetails) {
return LoginDetails.fromJson(json) as T;
} else if (T == UserDetails) {
return UserDetails.fromJson(json) as T;
} else if (T == Message) {
return Message.fromJson(json) as T;
} else if (T == bool || T == String || T == int || T == double) { // primitives
return json;
} else {
throw Exception("Unknown class");
}
}
static List<K> _fromJsonList<K>(List<dynamic> jsonList) {
return jsonList?.map<K>((dynamic json) => fromJson<K, void>(json))?.toList();
}
}
In order to add support for a new data model, simply add it to Generic.fromJson:
else if (T == NewDataModel) {
return NewDataModel.fromJson(json) as T;
}
This works with either generic objects:
Wrapper<Message, void>.fromJson(someJson)
Or lists of generic objects:
Wrapper<List<Message>, Message>.fromJson(someJson)