In java it would be something like this ...
public Request compose(LoginDevice login) {
JSONObject params = new JSONObject();
try {
if (login.granType != null)
params.put("grant_type", login.granType);
if (login.clientId != null)
params.put("client_id", login.clientId);
} catch (JSONException e) {
e.printStackTrace();
}
return (Request)new BaseRequest("oauth/v2/token", params.toString(), new HashSet());
}
And in Dart and tried something similar but it doesn't work... the parameter 'put' does not exist in JsonObjectLite...
Request compose(LoginDevice login)
{
JsonObjectLite params = new JsonObjectLite();
try {
if (login.granType != null) {
params.put("grant_type", login.granType);
}
if (login.clientId != null) {
params.put("client_id", login.clientId);
}
} on JsonObjectLiteException catch (e) {
print(e);
}
return new BaseRequest("oauth/v2/token", params.toString(), new HashSet());
}
How could I do it? Thank you
The class JsonObjectLite doesn't contain the method put.
How you can understand dart doesn't is Java, in this cases your the class JsonObjectLite has a method called putIfAbsent, the implementation is the following
/// If [isImmutable] is false, or the key already exists,
/// then allow the edit.
/// Throw [JsonObjectLiteException] if we're not allowed to add a new
/// key
#override
void putIfAbsent(dynamic key, Function() ifAbsent) {
if (isImmutable == false || containsKey(key)) {
_objectData.putIfAbsent(key, ifAbsent);
} else {
throw const JsonObjectLiteException('JsonObject is not extendable');
}
}
look also the Source code
So an example of code should be the following
import 'package:json_object_lite/json_object_lite.dart';
class AuthorAnswer {
var _username;
var _status;
AuthorAnswer(this._username, this._status);
String get username => _username;
String get status => _status;
}
int main() {
var author = '#vincenzopalazzo';
var sentences = 'Follow me on Github';
var authorObject = AuthorAnswer(author, sentences);
try{
JsonObjectLite params = new JsonObjectLite();
params.isImmutable = false;
params.putIfAbsent("author", () => authorObject.username);
params.putIfAbsent("sencence", () => authorObject.status);
print(params.toString());
} on JsonObjectLiteException catch (err){
print('--------- ERROR ----------');
print(err);
}
return 0;
}
You should be set the params.isImmutable = false and after you can add your propriety, with your logic.
In my opinion, I don't see any motivation to use this library, dart have 2 types of the module to implement the serialization, and I think should better use it because on the web exist the documentation, like this dart json, flutter json
Inside the flutter app, there are also the line guides, for the small application you can use dart:convert:json also for the other you can use the json_serializable
I want to add also an example of dart:convert
/**
*
*/
import "dart:core";
import "dart:convert";
class ClassToJsonOne {
var _propOne;
var _propTwo;
ClassToJsonOne(this._propOne, this._propTwo);
Map<String, dynamic> toJSon() => {
'prop_one': _propOne,
'prop_two': _propTwo
};
ClassToJsonOne.fromJson(Map<String, dynamic> json):
_propOne = json['prop_one'],
_propTwo = json['prop_two'];
#override
String toString() => 'First Class: $_propOne, $_propTwo';
}
class ClassToJsonTwo{
var _propOne;
var _propTwo;
ClassToJsonTwo(this._propOne, this._propTwo);
Map<String, dynamic> toJSon() => {
'prop_one': _propOne,
'prop_two': _propTwo
};
ClassToJsonTwo.fromJson(Map<String, dynamic> json):
_propOne = json['prop_one'],
_propTwo = json['prop_two'];
#override
String toString() => 'Second Class: $_propOne, $_propTwo';
}
main(List<String> args) {
print('------- Declare Objecr -------\n');
var objectToJsonOne = ClassToJsonOne('I am the fist object', 'empty');
var objectToJsonTwo = ClassToJsonTwo('I contains the first object', 'empty');
String jsonStringObjOne = jsonEncode(objectToJsonOne.toJSon());
print('\n---------- Object one JSON format ---------\n');
print(jsonStringObjOne);
String jsonStringObjTwo = jsonEncode(objectToJsonTwo.toJSon());
print('\n---------- Object one JSON format ---------\n');
print(jsonStringObjTwo);
print('\n---------- DECODE JSON to OBJECT ---------\n');
var fromJsonObjectOne = jsonDecode(jsonStringObjOne);
print(fromJsonObjectOne.toString());
var fromJsonObjectTwo = jsonDecode(jsonStringObjTwo);
print(fromJsonObjectTwo.toString());
}
Inside the classes, you can see the following methods
Map<String, dynamic> toJSon() => {
'prop_one': _propOne,
'prop_two': _propTwo
};
ClassToJsonTwo.fromJson(Map<String, dynamic> json):
_propOne = json['prop_one'],
_propTwo = json['prop_two'];
The result of the method toJSon, you should be pass to the method of the library jsonEncode and when you go to deserialize you can use the library method jsonDecode(youtStringJSOn) and the result you can pass to the method of your class fromJson.
In addition, you can configure the library json_serializable.
In conclusion, I want to fix my comment
I think the json_serializable worked how GSON, I can make an example for you, on this day.
On flutter, documentation has reported this text
Is there a GSON/Jackson/Moshi equivalent in Flutter?
The simple answer is no.
Such a library would require using runtime reflection, which is disabled in Flutter. Runtime reflection interferes with tree shaking, which Dart has supported for quite a long time. With tree shaking, you can “shake off” unused code from your release builds. This optimizes the app’s size significantly.
Since reflection makes all code implicitly used by default, it makes tree shaking difficult. The tools cannot know what parts are unused at runtime, so the redundant code is hard to strip away. App sizes cannot be easily optimized when using reflection.
Although you cannot use runtime reflection with Flutter, some libraries give you similarly easy-to-use APIs but are based on code generation instead. This approach is covered in more detail in the code generation libraries section.
you can found the source code inside this answer here
Related
In my flutter project, I want to use built_value for json serialization. One of the sample classes is:
import 'dart:convert';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'device.model.g.dart';
abstract class Device implements Resource, Built<Device, DeviceBuilder> {
// Fields
String get name;
String get code;
bool get isActive;
Device._();
factory Device([void Function(DeviceBuilder) updates]) = _$Device;
String toJson() {
return json.encode(serializers.serializeWith(Device.serializer, this));
}
factory Device.fromJson(String jsonString) {
return serializers.deserializeWith(
Device.serializer, json.decode(jsonString))!;
}
static Serializer<Device> get serializer => _$deviceSerializer;
}
I used the build_runner to generate the codes and everything is OK.
Also I have defined the serializers:
part 'serializers.g.dart';
final standardSerializers =
(serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();
#SerializersFor([
Device])
final Serializers serializers = _$serializers;
Now I want to use the toJson() method:
final device = Device((d) => d
..code = "DEV11"
..name = "Some Tools"
..isActive = true);
print(device.toJson());
The output is:
["name","Some Tools","code","DEV11","isActive",true]
which is a List of objects, instead of Map<String, dynamic>. The generated serializer is:
Iterable<Object?> serialize(Serializers serializers, Device object,
{FullType specifiedType = FullType.unspecified})
...
which returns an Iterable of Object?.
How can I make built_value to generate a json with the standard format, i.e:
{"name":"Some Tools","code":"DEV11","isActive":true};
P.S: using the dart encoder,
print(json.encode(device));
results in:
"[\"name\",\"Some Tools\",\"code\",\"DEV11\",\"isActive\",true]"
I made a silly mistake. Should be:
#SerializersFor([
Device,
])
final Serializers serializers =
(_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();
In the excellent Flutter book by Raywenderlich, Chapter 13 is dedicated to obtaining Responses from an api using libraries Chopper and JsonConverter.
The code in Github is here
He also proposes the use of a response wrapper like functional programming, of the type Success/Error.
The ModelConverter from Response to Success/Error wrapper, applies to the APIRecipeQuery model and only uses one method in one line final recipeQuery = APIRecipeQuery.fromJson(mapData);. It seems quite logical to make a generic convert, since it is a very useful class.
import 'dart:convert';
import 'package:chopper/chopper.dart';
import 'model_response.dart';
import 'recipe_model.dart';
class ModelConverter implements Converter {
#override
Request convertRequest(Request request) {
// Add a header to the request that says you have a request type of application/json using jsonHeaders.
// These constants are part of Chopper.
final req = applyHeader(
request,
contentTypeKey,
jsonHeaders,
override: false,
);
return encodeJson(req);
}
#override
Response<BodyType> convertResponse<BodyType, InnerType>(Response response) {
return decodeJson<BodyType, InnerType>(response);
}
Request encodeJson(Request request) {
final contentType = request.headers[contentTypeKey];
// Confirm contentType is of type application/json.
if (contentType != null && contentType.contains(jsonHeaders)) {
return request.copyWith(body: json.encode(request.body));
}
return request;
}
Response decodeJson<BodyType, InnerType>(Response response) {
final contentType = response.headers[contentTypeKey];
var body = response.body;
if (contentType != null && contentType.contains(jsonHeaders)) {
body = utf8.decode(response.bodyBytes);
}
try {
final mapData = json.decode(body);
if (mapData['status'] != null) {
return response.copyWith<BodyType>(
body: Error(Exception(mapData['status'])) as BodyType);
}
/*
The only line is next
*/
final recipeQuery = APIRecipeQuery.fromJson(mapData);
return response.copyWith<BodyType>(
body: Success(recipeQuery) as BodyType);
} catch (e) {
chopperLogger.warning(e);
return response.copyWith<BodyType>(body: Error(e) as BodyType);
}
}
So, I have tried by passing the model in the constructor as a parameter:
class ModelConverter <T extends JsonConverter> implements Converter {
final T model;
ModelConverter ({#required this.model});
and
I invoke it in recipe_service.dart with converter: ModelConverter(model: APIRecipeQuery), but I don't know how to reference the model statically, and can't access the method model.fromJson
Next, I have tried passing just the function converter:
class ModelConverter implements Converter {
Function fromJson;
ModelConverter ({# required this.fromJson});
with a getter in the API, and in recipe_service.dart with converter: ModelConverter(fromJson: APIRecipeQuery.fjConverter)
class APIRecipeQuery {
static Function get fjConverter => _ $ APIRecipeQueryFromJson;
But I can't get it to work.
What would be the best approach to make the ModelConverter generic?
Thnks in advance.
Solved in this post
model_converter.dart
. . .
typedef CreateModelFromJson = dynamic Function(Map<String, dynamic> json);
class ModelConverter<Model> implements Converter {
final CreateModelFromJson fromJson;
ModelConverter({#required this.fromJson});
. . .
final query = fromJson(mapData) as Model;
. . .
and recipe_service.dart
. . .
converter: ModelConverter<APIRecipeQuery>(
fromJson: (json) => APIRecipeQuery.fromJson(json),
),
. . .
I am making a pokemon app and I have a question that I already had in other projects and I would like to know if anyone can help me with a solution.
I receive a variable called pokemonName from other screen, I want to pass the variable and concatenate with the class "Strings", it will be like Strings.+pokemonName.toLowerCase(), converting to lowercase to get the map from the class Strings, but I don't know how to achieve this to remove the switch and don't need to use a lot of cases for each pokemon.
class PokemonDetailScreen extends StatelessWidget {
final String pokemonName;
final String image;
Map<String, dynamic> pokemonMap = {};
PokemonDetailScreen(this.pokemonName, this.image, this.index){
getPokemonMap();
}
#override
Widget build(BuildContext context) {
return Container();
}
void getPokemonMap(){
switch(pokemonName){
case "Bulbasaur":
pokemonMap = Strings.bulbasaur;
break;
case "Charmander":
pokemonMap = Strings.charmander;
break;
}
}
}
**Class in another dart file:**
class Strings {
static Map bulbasaur = {};
}
What I needed is something like this:
void getPokemonMap(){
pokemonMap = Strings.$pokemonMap.toLowerCase();
}
What you could do is have a static map indexed by the name of your Pokemons and whose values are maps.
class Strings {
static Map<String, dynamic> map = {
'Bulbasor': {},
'Charmander': {},
// ...
};
}
And you’ll use it like this: pokemonMap = Strings.map[pokemonName].
~You can use JSON file to do all this things instead use a class.~
I recommend not use a static class to do that thing, instead you can just make a normal class and instantiate on another file, so when the class that you call your another class will be dispose when the parent was.
class PokemonStrings {
Map bulbasaur = {your map here};
}
To call that in another file you need just do
PokemonString _pokemonString = PokemonString();
And call whatever you need in the class that you instantiate
var bulbasaurMap = _pokemonString.bulbasaur;
But even so you need walk with static class. Just call the name of class followed by dot to access all the static attributes
var bulbasaurMap = PokemonString.bulbasaur;
I have API communication service in my Flutter app with 10+ different services, and 100+ API calls that heed to parse data. In order to reuse code I've decided to create some common parsing code that is going to parse data from API:
ApiResponse handleObjectResponse({
#required http.Response serverResponse,
#required Function objectConstructor,
}) {
if (serverResponse.statusCode == 200) {
dynamic responseObject = objectConstructor(json.decode(serverResponse.body));
return ApiResponse(responseObject: responseObject);
} else {
ApiError error = responseHasError(serverResponse.body);
return ApiResponse(error: error);
}
}
This way I am able to parse JSON object from API in a reusable way no matter what the Object class is, just by passing constructor function to this method.
When I call this method in any of the Services I've created for fetching data like this:
handleObjectResponse(serverResponse: response, objectConstructor: ChartData.fromJson);
I get error: The getter 'fromJson' isn't defined for the class 'ChartData'.
Try importing the library that defines 'fromJson', correcting the name to the name of an existing getter, or defining a getter or field named 'fromJson'.
Where I think the problem is is in this model class and factory statement, but I don't know how to fix it:
class ChartData {
List<ChartDataPoint> points;
ChartData({
this.points,
});
factory ChartData.fromJson(Map<String, dynamic> json) {
List jsonPoints = json["data"];
return ChartData(
points: List.generate(jsonPoints.length,
(i) => ChartDataPoint.fromJsonArray(jsonPoints[i])));
}
}
You cannot pass constructors as functions. You need to create a function what will call the constructor instead:
(int a) => Foo(a);
Just a 2022 update: since 2.15 it's possible by Class.new, see the complete issue: https://github.com/dart-lang/language/issues/216.
class A {
final String a;
const A(this.a);
#override
String toString() => 'A($a)';
}
class B {
final String b;
const B(this.b);
#override
String toString() => 'B($b)';
}
void main() {
final List<Object Function(String)> constructors = [A.new, B.new];
for (final Object Function(String) constructor in constructors) {
final Object instance = constructor('My Constructor Parameter');
if (instance is A) {
print(instance.toString());
}
}
}
Note that if you're using named params, both class constructors must have the same param name, otherwise the constructor signatures won't match and then it will generate this static error:
The element type X can't be assigned to the list type Y.
I'm new in gwt. and need to know how to use JSON in gwt so i try this simple data loader but i'm still confuse.
I create a project named 'tesdb3' in eclipse. I create the PHP side to access the database, and made the output as JSON.. I create the userdata.php in folder war. then I compile tesdb3 project. Folder tesdb3 and the userdata.php in war moved in local server(I use WAMP). I put the PHP in folder tesdb3. This is the result from my localhost/phpmyadmin/tesdb3/userdata.php
[{"kode":"002","nama":"bambang gentolet"}{"kode":"012","nama":"Algiz"}]
From that result I think the PHP side was working good.Then I create UserData.java as JSNI overlay like this:
package com.tesdb3.client;
import com.google.gwt.core.client.JavaScriptObject;
class UserData extends JavaScriptObject{
protected UserData() {}
public final native String getKode() /*-{ return this.kode; }-*/;
public final native String getNama() /*-{ return this.nama; }-*/;
public final String getFullData() {
return getKode() + ":" + getNama();
}
}
Then Finally in the tesdb3.java:
public class Tesdb3 implements EntryPoint {
String url= "http://localhost/phpmyadmin/tesdb3/datauser.php";
private native JsArray<UserData> getuserdata(String json)
/*-{
return eval(json);
}-*/;
public void LoadData() throws RequestException{
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
builder.sendRequest(null, new RequestCallback(){
#Override
public void onError(Request request, Throwable exception) {
Window.alert("error " + exception);
}
public void onResponseReceived(Request request,
Response response) {
//1
//data(getuserdata(response.getText()));
//2
JsArray<UserData> uda = JsonUtils.unsafeEval(response.getText())
data(uda);
}
});
}
public void data(JsArray<UserData> data){
for (int i = 0; i < data.length(); i++) {
String lkode =data.get(i).getKode();
String lname =data.get(i).getNama();
Label l = new Label(lkode+" "+lname);
tb.setWidget(i, 0, l);
}
RootPanel.get().add(new HTML("my data"));
RootPanel.get().add(tb);
}
public void onModuleLoad() {
try {
LoadData();
} catch (RequestException e) {
e.printStackTrace();
}
}
}
The result from both method i use in the onResponseReceived is the same. Just showing string "my data". but the method 2 create eror like this:
14:41:59.875 [ERROR] [tesdb3] Uncaught exception escaped
com.google.gwt.core.client.JavaScriptException: (SyntaxError): syntax error
Did I miss use the 2nd method? Why method 1 didn't have any eror but can't show the data?
The problem is that your JSON has incorrect syntax, you are missing a comma after the first item of the table, it should be (whitespace added for readability):
[
{
"kode": "002",
"nama": "bambang gentolet"
},
{
"kode": "012",
"nama": "Algiz"
}
]
Since JSON is a part of JavaScript this might be the syntax error exception you are getting.
PS: I'd recommend using some PHP framework to create JSON for you (Zend Framework is my usual choice). Also, JSON validators like JSONLint are very useful for debugging JSON.
It looks like a typo in your code, which brings me to naming conventions: for variables and methods use camel case, starting with a lower case character. Thus UserData UD should be UserData ud.
In your getuserdata method (which should be getUserData) you use the parameter name Json with capital J and in the native code json with the lower j. This explains the error.
Regarding the getUserData method. There is a GWT method: JsonUtils.unsafeEval(json) which you should use.
Furthermore, the code in the onResponseReceived seems incomplete, it might not be of importance as it might be incorrectly be put in this example, but just to be complete, here is what it should look like:
JsArray<UserData> uda = JsonUtils.unsafeEval(response.getText());
for (int i = 0; i < uda.length(); i++) {
UserData ud = uda.get(i);
String lKode = ud.getKode();
String lName = ud.getNama();
Label l = new Label(lKode + " " +lName);
RootPanel.get().add(l);
}