The upcoming Dart 2.X release requires strong typing. When working with JSON data we must now cast dynamic types to an appropriate Dart type (not a problem).
A related question Ignoring cast fail from JSArray to List<String> provides the answer to use the .cast<String>() function. Also a recent group messages says the same:
Breaking Change: --preview-dart-2 turned on by default.
The problem is that the .cast() function doesn't seem to compose. This original code when compiled using DDC and run in the Chrome browser:
Map<String, dynamic> json = { "data": ["a", "b", "c"] };
List<String> origBroken = json["data"].map( (s) => s.toUpperCase() ).toList();
Now receives the runtime warning (which will soon be an error)
Ignoring cast fail from JSArray to List<String>
So I add the .cast<String>() as the documentation and related link suggest and still receive the warning:
List<String> docFixBroken = json["data"].cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> alsoBroken = List.from( (json["data"] as List).cast<String>() ).map( (s) => s.toUpperCase() ).toList();
The code that doesn't give the warning requires a temporary variable (and also seems to be able to skip the explicit cast):
List<String> temp = json["data"];
List<String> works = temp.map( (s) => s.toUpperCase() ).toList();
So how can I write the cast and map as a single composed expression? The reason I need it as a single expression is that this expression is being used in an initializer list to set a final class variable.
I wrote Ignoring cast fail from JSArray to List<String>, so let me try and help here too!
So I add the .cast<String>() as the documentation and related link
suggest and still receive the warning:
List<String> docFixBroken = json["data"].cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> alsoBroken = List.from( (json["data"] as List).cast<String>() ).map( (s) => s.toUpperCase() ).toList();
Unfortunately, List.from does not persist type information, due to the lack of generic types for factory constructors (https://github.com/dart-lang/sdk/issues/26391). Until then, you should/could use .toList() instead:
(json['data'] as List).toList()
So, rewriting your examples:
List<String> docFixBroken = json["data"].cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> alsoBroken = List.from( (json["data"] as List).cast<String>() ).map( (s) => s.toUpperCase() ).toList();
Can be written as:
List<String> notBroken = (json['data'] as List).cast<String>().map((s) => s.toUpperCase()).toList();
Hope that helps!
Related
Hi i am new to the Dart programming language, i have been looking at this code for local persistence for a journal app. My confusion came when i came across this:
Map<String, dynamic> toJson() => {
"journals": List<dynamic>.from(journal.map((x) => x.toJson())),
};
What confuses me is what does this translate to, i think it translates to the following:
Map<String, dynamic> toJson() {{
"journals": List<dynamic>.from(journal.map((x) => x.toJson())),
};}
Which is wrong (because Vs Code is shouting # me), i also thought toJson() is simply a Map variable but again that is wrong,
now if it is a method that means its return type is Map<String, dynamic>, but then where is the return statement in the body of the function?
,my other question is what does it really translate to if we are to remove the short hand, and help me understand what kind of function ends with a semi colon like that, please help a newbie
It "translates" (as in: means the same thing as)
Map<String, dynamic> toJson() {
return {
"journals": List<dynamic>.from(journal.map((x) => x.toJson())),
};
}
The function returns a single value, which is a Map literal.
Map literals have the form {key: value, ..., key: value}.
This can be confusing to people coming from modern JavaScript, because JavaScript introduced a function shorthand where (x) => { ... } is shorthand for function(x) { ... }. The body of a JavaScript => is a statement block. The content of a Dart => is a single expression, the value of which is returned.
I wonder, if a dart or flutter method exists, which returns complete type information about the structure of a variable's value as a string.
E.g., if some print( someValue.toString() ); emits this
{"user":"userName","state":"valid"}
I don't know if it is a Map or a String.
But how do I get a string, which describes a variable's value / structure?
Something, that's like PHP's print_r(), which print stuff like this:
Array
(
[a] => Apfel
[b] => Banane
[c] => Array
(
[0] => x
[1] => y
[2] => z
)
)
There is nothing like print_r() available natively in Dart.
However, it would be possible to use the following functionalities to build e.g. a function like print_r() from PHP.
You can evaluate the type of someValue using runtimeType.
String someValueType = someValue.runtimeType.toString();
String someValueString = someValue.toString();
If you want to compare, you can also use is. More on that here.
Dart is a strongly typed language, which means that you could also enforce types (List a; String b), which makes a function like print_r() redundant for Dart.
If it is about server-side code you can use reflection to create a function that produces such an output for every value.
If it is about your own classes, you can implement toString() to make the objects render themselves this way when they are printed
class Person {
Foo(this.firstName, this.lastName);
String firstName;
String lastName;
#override
String toString() => '''
firstName: $firstName
lastName: $lastName
''';
}
print(new Person('John', 'Doe'));
Best way to do is
print(”$someValue“);
It will return a structured string which similar to JSON.
In my question Dart 2.X List.cast() does not compose the answer requires converting a List<dynamic> to a List<String> as such:
List<String> ls = (json['data'] as List).cast<String>().map((s) => s.toUpperCase()).toList();
My experience from other languages had me write this first:
List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();
Note that this compiles but fails at runtime in Dart 2.
Why does Dart typecasting for a List require a function as List).cast<String>() as opposed to simply using the Dart as "typecast operator" such as as List<String>?
---- Edit ----
I am using the most recent Dart 2.0.0-dev.43.0 and get inconsistent runtime behavior with as typecasts/assertions. Isn't the .cast<>() function creating a new iterable the same as a .map()? Changing my code to this works:
List<String> ls = (json['data'] as List).map((s) => (s as String).toUpperCase()).toList();
This seems to take advantage that the first cast to List is a List<dynamic>. Thus the .map function parameter is also a dynamic.
My second example above with to as List<String> works in some places in our code but not others. Note that IntelliJ correctly infers the types in all of the above examples - it's the runtime where the failure happens. I'm guessing that the inconsistent behavior is due to Dart 2.x being still in development.
---- 2nd Edit ----
Here are my test cases that I have in one of my class constructors:
Map<String, dynamic> json = { "data": ["a", "b", "c"] };
//List<String> origBroken = json["data"].map( (s) => s.toUpperCase() ).toList();
// Sometimes works - sometimes gives "Ignoring cast fail from JSArray to List<String>" at runtime!!!
List<String> wonky = (json["data"] as List<String>).map( (s) => s.toUpperCase() ).toList();
print("Wonky $wonky");
List<String> fix1 = (json["data"] as List).cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> fix2 = (json["data"] as List).map( (s) => (s as String).toUpperCase() ).toList();
List<String> explicit2 = (json["data"] as List<dynamic>).map( (dynamic s) => (s as String).toUpperCase() ).toList();
// From accepted answer of the linked question - compile error because .cast() doesn't take parameters
// error: Too many positional arguments: 0 expected, but 1 found.
//List<String> notBroken = (json['data'] as List).cast<String>((s) => s.toUpperCase()).toList();
List<String> notBrokenFixed = (json['data'] as List<String>).cast<String>().map((String s) => s.toUpperCase()).toList();
The problem is the warning Ignoring cast fail from JSArray to List<String> sometimes given by the wonky assignment. When I say sometimes it's because it changes unpredictably as I make changes to the main application that uses the library that contains this code - without making changes to this class or even the library.
At the time I wrote the first edit above, wonky wasn't working. I just tried it again now and it's working. I have not changed any code in this library - I have been working in the main application which has a dependency on this code's library.
Some background, this is a multi-library project being converted from Angular/Typescript. These test cases are based on the processing we do to deserialize JSON into Dart classes. We map JSON (dynamic) strings into various data structures such as enums, Option<> and Either<> (from dartz) using class constructor initializers.
A couple weeks ago the runtime warning started happening I believe because of Breaking Change: --preview-dart-2 turned on by default.
I understand that this warning will soon be an error. So I traced the warning back to these conversions that map from JSON dynamic data (Yes, dynamic data is an edge case in Dart but it's what dart:convert provides).
We are developing on Mac using DDC with the most recent Dart 2.0.0-dev.43.0, angular 5.0.0-alpha+8, build_runner 0.8.0, IntelliJ 2018.1 and running on Chrome 65.0.3325.181.
---- Final Edit ----
There is an instability in the current development build/runtime that is behind this issue. No, I don't have a reproducible example. Changing and rebuilding our main app will cause this code in an unmodified library dependency to sometimes give the runtime warning Ignoring cast fail from JSArray to List<String>.
The suspect code from the original part of this question (also wonky above)
List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();
casts the dynamic JSON data to a List<String>. The types are fully constrained and the Dart analyzer/IntelliJ infers s to be Static type: String.
The runtime warning that sometimes occurs and related answers to use .cast() is what led to this question. At this time I'll believe the analyzer and ignore the runtime warning.
In Dart 2 generic types are reified.
as ... is more like an assertion, if the values type doesn't match as causes a runtime exception.
cast<T>() is a method introduced in Dart 2 on Iterable that actually creates a new iterable of type Iterable<T> (or in your case the subclass List<T>) filled with the values of the original interable.
Update
You can use print('wonky: ${wonky.runtimeType}'); to see what the actual type is.
If the type matches your requirement, you can use as to communicate it to the analyzer that it's safe to assume this type.
If the type doesn't match, for example because it is List instead of List<String>, then you can use .cast<String>() to actually make it a List<String>.
List<String> broken = (json['data'] as List)
.cast<String>((s) => s.toUpperCase()).toList();
Here you seem to try to use cast for casting and mapping, but that is not valid.
map() can do both though
List<String> notBroken = (json['data'] as List)
.map<String>((s) => s.toUpperCase()).toList();
I have some code in a repository base class for Entity Framework that eager loads Navigation properties:
public virtual List<T> Find(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties)
{
//blah biddy blah blah
}
Then when calling the above method:
var beers = BeerRepository.Find(x => x.Type == "IPA", a => a.Ingredients, b => b.Sizes, c => c.Hangovers);
It works great. I know that using "params" provides a great magic shortcut when calling the method and I've seen some SIMPLE examples of what would be needed without it.
But, I'm having trouble figuring out how to call the method above when I remove params from the signature.
Any thoughts?
A generic method is a method template. If you supply a type argument, it becomes a concrete, typed, method. Your method (without params)...
public virtual List<T> Find<T>(Func<T, bool> where,
Expression<Func<T, object>>[] navigationProperties)
...in BeerRepository will turn into something like...
public virtual List<Beer> Find(Func<Beer, bool> where,
Expression<Func<Beer, object>>[] navigationProperties)
...which clearly shows you have to provide a Expression<Func<Beer, object>>[] array. It takes a bit more clunky code to build that, because you can't take advantage of type inference:
var navProps = new Expression<Func<Beer, object>>[]
{
a => a.Ingredients,
a => a.Sizes,
a => a.Hangovers
});
Not sure I understand your question.
You can just call
beerRepository.Find((x => x.Type == "IPA")
And then inside Find() you'll see that the navigationProperties array will be empty.
I have a class that returns a Zend\Db\Sql\Select object.
Inside the test with phpunit I try to get the sql string via
var_dump($select->getSqlString());
But I get an error message:
Attempting to quote a value without specific driver level
support can introduce security vulnerabilities in a
production environment.
Inside my bootstrap for phpunit I define
putenv('APP_ENV=test');
I also tried
var_dump($select->getSqlString(new \Zend\Db\Adapter\Platform\Mysql()));
but get a similar error
Attempting to quote a value in Zend\Db\Adapter\Platform\Mysql
without extension/driver support can introduce security
vulnerabilities in a production environment.
What can I do to get the string in my test without a database adapter/driver?
$adapter = new \Zend\Db\Adapter\Adapter ([
'driver' => 'Pdo',
'dsn' => 'sqlite:./test.sql',
]);
$where = new \Zend\Db\Sql\Where();
$where->orPredicate (new \Zend\Db\Sql\Predicate\Like('table.cat1', 1))
->orPredicate (new \Zend\Db\Sql\Predicate\Like('cat2', 2));
$sql = new \Zend\Db\Sql\Sql ($adapter);
$select = $sql->select ();
$select->from ('table')->where ($where);
echo $sql->getSqlStringForSqlObject ($select);
#acond
Since
$sql->getSqlStringForSqlObject ($select)
is deprecated, better use
$sql->buildSqlString($select)