Can I create a const object from a json string? - dart

I want to create a const object from a fixed JSON string.
This json string is comming from a --dart-define parameter.
I'm getting it using const _APP_CONF = String.fromEnvironment('APP_CONF', defaultValue: '{}');
I've tried the code below, but it is not working. the compiler is complaining about the second constructor:
class AuthnProvider {
final String id;
final String clientId;
final List<String> scopes;
const AuthnProvider(
{this.id,
this.clientId,
this.scopes});
const AuthnProvider.fromJson(final Map<String, dynamic> json)
: id = json['id'],
clientId = json['clientId'],
scopes = json['scopes'].cast<String>();
The json parameter is coming from json.decode() method.
I also tried to create const and final var from from the json map and use the first constructor, but compiler gives error too.

This is expected. const create a compile-time constant. Dart does not execute code during compilation and therefore cannot create a const from a Map. This is the reason const constructors can't have a body and why there is no other way to work around this limitation.
You don't mention the reason for doing this in your question, but if it's for performance, the difference will be negligible. If it's for immutability, all the fields we see are already final, so making the object const makes no difference.

Related

Dart Annotation - Unable to pass non constant method

How can I pass-in non-constant method in Dart Annotations?
Here is my annotation:
class Injectable {
final String name;
final Scope scope;
final List<Provider>? provider;
const Injectable({this.name = '', this.scope = Scope.factory, this.provider});
}
class Provider<T> {
final Type? useClass;
final Object? useValue;
final T? Function()? usefactory;
final List<Type> deps;
const Provider(
{this.useClass, this.useValue, this.usefactory, this.deps = const []});
}
This works fine:
But when I try to directly pass-in the function. I am getting a compile error:
Any idea what is happening?
Error: Arguments of a constant creation must be constant expressions. (Documentation) Try making the argument a valid constant, or use 'new' to call the constructor.
Invalid constant value.
The argument type 'ServiceA' can't be assigned to the parameter type 'ServiceA? Function()?'. (Documentation)
Please try to remove the brackets from inject<ServiceA>(). Just make it inject<ServiceA>.
usefactory-s type is a function, but in your case, you are passing ServiceA type data.

Converting complex JSON string to Map

When I say complex, means : A lot of nested objects, arrays, etc...
I am actually stuck on this simple thing:
// Get the result from endpoint, store in a complex model object
// and then write to secure storage.
ServerResponse rd = ServerResponse.fromMap(response.data);
appUser = AppUser.fromMap(rd.data); // appData is a complex object
await storage.write(key: keyData, value: userData);
String keyData = "my_data";
const storage = FlutterSecureStorage();
String? userData = appUser?.toJson(); // Convert the data to json. This will produce a JSON with escapes on the nested JSON elements.bear this in mind.
// Now that I stored my data, sucessfully, here comes the challenge: read it back
String dataStored = await storage.read(key: keyData);
// Now What ?
If I decide to go to appUser = AppUser.fromJson(dataStored), will be very complicated because for each level of my Json, too many fromJson, fromMap, toJson, toMap...It's nuts.
Hovever I've a fromMap that's actually works good since Dio always receive the data as Map<String, dynamic>. And my question is: Is there some way to convert a huge and complex JSON stringified to a full Map<String, dynamic> ? json.decode(dataStored) only can convert the properties on root - Nested properties will still continue as JSON string inside a map.
Any clue ??? thanks !
This is the main problem since Dart is lacking data classes. Thus, you need to define fromJson/toJson methods by yourself. However, there are several useful packages that use code generation to cope with this problem:
json_serializable - basically a direct solution to your problem.
freezed - uses json_serializable under the hood, but also implement some extra useful methods, focusing on the immutability of your data classes.
Other than that, unfortunately, you would need to implement fromJson/toJson methods by yourself.
This website might help you.
Simply just paste your json in there and it will automatically generate fromJson/toJson methods for you, then you can custom it by yourself.
I've used it a lot for my company project and it's very helpful.
Here's the link to website : link
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
const userJson = {
'firstName': 'John',
'lastName': 'Smith',
};
class User {
final String firstName;
final String lastName;
const User({required this.firstName, required this.lastName});
Map<String, dynamic> toJson() => {
'firstName': firstName,
'lastName': lastName,
};
factory User.fromJson(dynamic json) {
return User(
firstName: json['firstName'] as String? ?? '',
lastName: json['lastName'] as String? ?? '',
);
}
}
void main() {
test('from and to json methods', () async {
final user = User.fromJson(userJson);
final _userJson = user.toJson();
debugPrint(userJson.toString());
debugPrint(_userJson.toString());
expect(const MapEquality().equals(userJson, _userJson), true);
});
}

How to convert list to map in dart?

I'm new to dart. I'm trying to covert my Holiday class to Map to be used in my calendar. I tried using Map.fromIterable but it only convert it to <String, dynamic>?
class Occasion {
final List<Holiday> holidays;
Map<DateTime, List> toMap() {
var map = Map.fromIterable(holidays,
key: (e) => DateFormat('y-M-d').format(DateTime.parse(e.date)),
value: (e) => e.name);
print(map);
}
}
class Holiday {
final String date;
final String name;
Holiday({
this.date,
this.name,
});
factory Holiday.fromJson(Map<String, dynamic> parsedJson) {
return Holiday(date: parsedJson['date'], name: parsedJson['name']);
}
}
There are two things:
First: The type parameters of your returned map aren't right for the values you produce in fromIterable. You say it should be List, but in value: ... you are only producing a single String.
Secondly, as I said in my comment you need to help out the dart compiler here a little bit. The compiler isn't very smart. It doesn't see that you are only producing Strings in value. You need to tell him that.
To be fair. This might not be the problem of the compiler, but an overuse of the dynamic type in the collections library.
Map<String, String> toMap() {
var map = Map<String, String>.fromIterable(holidays,
key: (e) => e.date,
value: (e) => e.name );
return map;
}
Just remember: be precise with your types. If you run into type errors start putting additional type information everywhere you can. If you feel it's to cluttered after that, try removing them one spot at a time and see where it leads you.

Difference between class constructor syntax in Dart

I have a class that I am creating that looks like this:
class Movie {
final String title, posterPath, overview;
Movie(this.title, this.posterPath, this.overview);
Movie.fromJson(Map json) {
title = json["title"];
posterPath = json["poster_path"];
overview = json['overview';
}
}
I am getting a warning that says that "The final variables 'overview', 'posterPath', & '1' more must be initialized. There are also warnings around each variable saying 'title' can't be used as a setter because it is final.
When I write the constructor using this syntax, the warnings go away:
Movie.fromJson(Map json)
: title = json["title"],
posterPath = json["poster_path"],
overview = json['overview'];
What exactly is going on here?
Dart objects must be fully initialized before anyone gets a reference to the new object. Since the body of a constructor can access this, the object needs to be initialized before entering the constructor body.
To do that, generative Dart constructors have an initializer list, looking similiar to C++, where you can initialize fields, including final fields, but you cannot access the object itself yet. The syntax:
Movie.fromJson(Map json)
: title = json["title"],
posterPath = json["poster_path"],
overview = json['overview'];
uses an initializer list (the list of assignments after the :) to initialize the final instance variables title, posterPath and overview.
The first constructor uses an "initializing formal" this.title to directly put the parameter into the field.
The constructor
Movie(this.title, this.posterPath, this.overview);
is effectively a shorthand for:
Movie(String title, String posterPath, String overview)
: this.title = title, this.posterPath = posterPath, this.overview = overview;
Your constructor can combine all of these and a body:
Movie(this.title, this.posterPath, String overview)
: this.overview = overview ?? "Default Overview!" {
if (title == null) throw ArgumentError.notNull("title");
}
(A const constructor cannot have a body, but it can have an initializer list with some restrictions on the allowed expressions to ensure that they can be evaluated at compile-time).
Dart separates properties initialization from the constructor body.
A constructor has 3 parts :
the name/parameters definition
properties initialization/super call/asserts
A body, similar to a function immediately run on construction
Both the initialization and body parts are optional.
final variables must be initialized on the first 2 parts. They cannot be initialized inside the body.
A full constructor will look like the following :
MyClass(int value)
: assert(value > 0),
property = value,
super();
{
print("Hello World");
}
The main purpose of this initializer part is for body-less constructors which allows const constructors, a dart specific feature. See How does the const constructor actually work? for more details on these.
I just found some documentation around this, & it seams that the second version with the : is what's called the "initializer list" which allows you to initialize instance variables before the constructor body runs.
There is more detail around this in the documentation here.

Can someone illustrate how to use const constructor, and under what circumstances do I need to use it? [duplicate]

This question already has answers here:
How does the const constructor actually work?
(5 answers)
Closed 8 years ago.
I've just started to learn dart, but I can't understand const constructor. Can someone illustrate how to use const constructor.
Under what circumstances do I need to use it?
Did you happen to stumble upon Chris Strom's post about constant constructors? What Chris Strom does in the article is an explanation of final fields, but scroll down to the comments section and there's a nice clarification of constant constructors from a certain Lasse.
Const objects are used in annotations:
import 'dart:mirrors';
class Foo{
final name;
const Foo(this.name);
}
#Foo("Bar")
class Baz{}
void main() {
ClassMirror cm = reflectClass(Baz);
print(cm.metadata.first.getField(#name).reflectee); // prints Bar
}
Why const objects were introduced(from the dev team):
Why does Dart have compile time constants?
Also they can provide extra optimization. For example, my dar2js experiment:
Does dart2js optimize const objects better?
Some specifics:
class Foo{
final baz;
const Foo(this.baz);
}
void main() {
//var foo = const Foo({"a" : 42}); //error {"a" : 42} is a mutable Map
var foo = const Foo(const {"a" : 42}); //ok
//foo.baz["a"] = 1; //error Cannot set value in unmodifiable Map
var bar = new Foo({"a" : 42}); //ok
//bar.baz = {"c" : 24}; //error NoSuchMethodError: method not found: 'baz='
bar.baz["a"] = 1; //ok;
}
If class has only const constructor you still can create mutable object with new
final baz is immutable reference. But since bar is mutable, you can change baz object.

Resources