Flutter: Object generation from jSON with dynamic variables - json

With dart / flutter it is possible to instantiate an Dart object from a jSON object.
For example, you define the dart class and instantiate an object of that class with the content from a jSON string via the API query.
The question is:
Is there any way that the jSON object contains additional variables that are then also available in the instantiated dart object? The goal is an ultra dynamic App, which is only supplied by the backend.
So far, I've been working with 'fixed' variable names or lists of subobjects.
This approach works perfectly smoothly.
I have not tried the approach of a dynamic variable from a jSON string yet.
I would like to ask if this is possible in principle before I design a concept based on this approach.
{
"widgetType": "radioGroup",
"label": "Level",
"integerValueNew": 200
}
#JsonSerializable()
class Input {
Input();
#JsonKey(required: true) String widgetType;
String label;
factory Input.fromJson(Map<String,dynamic> json) => _$InputFromJson(json);
Map<String, dynamic> toJson() => _$InputToJson(this);
}
In the Dart class only the variables 'widgetType' and 'label' are defined. Also the type of the variable is known, when an objekt is instantiated.
Can a dart object be instantiated from the shown jSON object, which then also contains the integer 'integerValueNew'?
If that works, you can develop a concept for it.
If not, I would work with sub-objects and well-defined variable names in Dart classes. Then the concept has to be broken down to a simple key value mapping.
I also do not know how the dart object should know which type of variable it is at all.

Related

Flutter - What is the best way to parse Json?

Actually, I am using the traditional way to work with Json:
factory MyObject.fromJson(Map<String, dynamic> json)
I have a lot of objects dealing with Json and over time, I encounter problems like:
Converting object to an encodable object failed: Instance of 'MyObject'#0
I am looking for the best way (external plugin or something else) to manipulate these Json.
Take a look on json_serializable package.
And docs has an excellent resource about JSON serialization.
This is how I would set up the MyObject class to parse Json
class MyObject {
String value;
MyObject({this.value});
static MyObject fromMap(Map<String,dynamic> map){
var value = map['value'];
return MyObject(value:value);
}
}

Dart: Generic which has .fromJson constructor

In Swift I'm used to setting up a protocol JSONInitializable which defines an init?(json: JSON) meaning that all classes and structs that conform to the protocol are initializable with a JSON object.
Is it true that this isn't possible in Dart (with an abstract class) because static methods and factory initializers are not inherited from an abstract class?
The reason I'm asking is because I'm writing a lot of similar methods for API GET requests, which could easily be merged into one, e.g.:
static Future<T> get<T extends JSONInitializable>(int id) async {
final resourceName = T; // TODO: transform to snake case
final uri = Uri.parse("$kApiHostname/api/$resourceName/$id");
final response = await _secureGet(uri);
if (response == null) {
return null;
}
final responseJson = json.decode(response.body);
final model = T.fromJson(responseJson);
return model;
}
But for that to work I'd need to constrain T to a protocol/interface that defines the .fromJson() initializer.
The feature you want is not available (or planned) in Dart, but there have been discussions on it.
Give this issue a thumbs up: https://github.com/dart-lang/language/issues/356
A MANUAL workaround could be having a map of serializers,deserializers like:
//Register object 1 in singleton
JsonMapper.register<MyObject>(JsonObjectMapper(
(mapper, map) => MyObject.fromJson(map),
(mapper, instance) => instance.toJson(),
));
//Register object 2 in singleton...
...
This way you can deserialize and serialize your objects as long as you have them registered without resorting to it's generic type.
The AUTOMATIC (technically code generated) way would be using a package like simple_json to help you workaround this with a little code generation so you don't mess up registering and eliminating mappers.
And the big plus is that the actual code that transforms your objecto from and to JSON is not stored in the object itself but in the generated classes, thus pushing the responsability of serializaition deserailization from the object into the generated code (in a decoupled manner).
Take my advice with a grain of salt as with both approaches you lose static type checking of wether a type has a registered mapper and can be converted.

Custom deserializer for generic list

I'm trying to create a custom deserializer for generic lists. Lets say I get a json representation of class B:
public class B{
List<A> listObject;
}
where A is some other class which I see only at runtime. I'd like to create a deserializer that will be able to infer the type of listObject as list with inner type A and deserialize it as such instead of using the default hashmap deserializer.
I tried using contextual deserializer, similar to what was suggested here
and then adding it as a custom deserializer for List
addDeserializer(List.class, new CustomListDeserializer())
But I'm not sure how am I supposed to read the json and create the list in deserialize function (in the Wrapper example above it's pretty simple, you read the value and set it as a value field, but if my 'wrapper' is List, how do I read the values and add them?)
I tried using readValue with CollectionType constructed with constructCollectionType(List.class, valueType) but then I go into an infinite loop, since readValue uses the deserializer from which it was called.
Any ideas?
Thanks for the suggestion. I solved it by parsing the json as an array of inner generic type and then converting to list, as follows:
Class<?> classOfArray = Array.newInstance(valueType.getRawClass(), 0).getClass();
Object[] parsedArray = (Object[]) parser.getCodec().readValue(parser, classOfArray);
return Arrays.asList(parsedArray);

My RCP Client is not returning a deep copy of an object

I have been working on an RCP Client to handle weather data.
What i do is 2 things, first i scraped the JSON i will be using and put it into a dart file. See: https://dartpad.dartlang.org/a9c1fe8ce34c608eaa28
My server.dart page, will import the weather data, and then carry out the following:
import "dart:io";
import "weather_data.dart";
import "dart:convert";
import "package:rpc/rpc.dart";
find ApiServer _apiServer = new ApiServer(prettyPrint:true);
main() async {
Weather w = new Weather(WeatherJson);
TestServer ts = new TestServer(w);
_apiServer.addApi(ts);
HttpServer server = await HttperServer.bind(InternetAddress.ANY_IP_V4, 12345);
server.listen(_apiServer.httpRequestHandler);
}
class Weather{
Map weather;
Weather(this.weather){
Map get daily => weather["daily"];
}
}
#ApiClass(name:"test_server", version: 'v1', description: 'This is a test server api to ping for some quick sample data.')
class TestServer {
Weather myWeather;
TestServer(this.myWeather){
}
#ApiMethod(method:'GET', path: 'daily')
Map<String, Object> getDaily(){
return myWeather.daily;
}
}
So, the server starts correctly, and i will go to localhost:12345/test_server/v1/daily and it will return this:
{
"summary": {},
"icon": {},
"data": {}
}
which is not correct. If you look up the JSON data, summary and icon are both strings and data is an array. They are also empty, and should contain the data i wanted to return.
Why does this occur? Is it because i am returning a Map<String, Object>? I was trying to set it up to be: Map<String, dynamic> but the dart compiler didnt like it.
How do i get this data to return the correct dataset?
The Dart website for RPC is located at: https://github.com/dart-lang/rpc
and you can see that under methods, the return value of a method can be either an instance of a class or a future. That makes sense as per usual, so I set it to be a Map<String,Object> though trying to be vague about it by saying: Map was not sufficient.
Edit:
When doing this mostly in dart pad without RPC, it seems to work correctly, by a sample of: https://dartpad.dartlang.org/3f6dc5779617ed427b75
This leads me to believe something is wrong with the Parsing tool as it seems the return type in dartpad allows to return Map, Map<String, Object>, and Map<String, dynamic>.
Having had a quick look at the RPC package README here https://pub.dartlang.org/packages/rpc, it seems that methods marked as Api methods (with #ApiMethod) should return an instance of a class with simple fields such as:
class ResourceMessage {
int id;
String name;
int capacity;
}
The RPC package will take that instance and serialize it into JSON based on the field names.
From the README:
The MyResponse class must be a non-abstract class with an unnamed
constructor taking no required parameters. The RPC backend will
automatically serialize all public fields of the the MyResponse
instance into JSON ...
You are returning a nested Map representation of the JSON you want the RPC operation to emit and would guess that the RPC package does not handle it as you are expecting it to.
Re: this from your question:
This leads me to believe something is wrong with the Parsing tool as
it seems the return type in dartpad allows to return Map, Map, and Map.
There is no 'parsing' on JSON going on on your example. The data you have is a set of nested literal Dart Maps, Lists and Strings with the same structure as the JSON it was derived from. It just happens to look like JSON.
In your example you are just selecting and printing a sub-map of your data map (data['daily']), which prints out the String that results from calling toString() - which is recursive so you get the contents of all the nested maps and lists within it.
So it's not a 'deep copy' issue, but a difference in how toString() and the RPC code processes a set of nested maps.
BTW: the return type of your getDaily() method is immaterial. What is returned is just a Map whatever the declared return type of the method is. Remember types in Dart are optional and there for editors and compilers to spot potentially incorrect code. See https://www.dartlang.org/docs/dart-up-and-running/ch02.html#variables.
I am going to piggyback off of #Argenti Apparatus here as there was a lot of information gained from him.
Long story short, the required return type of the method:
#ApiMethod(method:'GET', path: 'daily')
Map<String,Object> getDaily(){ // <-- Map<String,Object>
return myWeather.daily;
}
is the error.
I went through and updated the method signature to be Map<String,String> and it parsed it entirely correct. It did not parse the object as a string, but actually parsed it as a full recursed object.
I went through and for the sake of code cleanliness also changed signatures of Weather properties to reflect what they actually were, Map<String,Object> as well.
All in all, When defining it to be an value type of Object, it was returning curly braces, but setting it as a String parsed it correctly.
I ran it through JSLint to confirm it is correct as well.
I gave a +1 to the helper, I had to dig deeper into the code to see WHY it wasnt doing a Map correctly.
This also I feel, is plausibly a bug in RPC Dart.

ExtJS - convert json to Model

I have a json string that I need to map into a Model, I've been checking this json reader, Ext.data.reader.JsonView, but it seems that it only wokrs with a proxy, I need to pass a string (which contains the json) and map it to my model. Is that possible?
Thanks,
Angelo.
Take a look on the Ext.data.model's constructor.
http://docs.sencha.com/extjs/4.2.3/#!/api/Ext.data.Model-method-constructor
You can pass your data into it and it will map it to your model's fields. So you can do something like:
var model = new Ext.data.model(Ext.decode(<yourJsonString>));
Ext.data.model can be replaced with your model class.