Here is a json file person.json
{
"first_name": "John",
"last_name": "Doe"
}
Here is the Person class
import 'package:json_annotation/json_annotation.dart';
part 'person.g.dart';
#JsonSerializable()
class Person {
/// The generated code assumes these values exist in JSON.
final String first_name, last_name;
/// The generated code below handles if the corresponding JSON value doesn't
/// exist or is empty.
final DateTime? dateOfBirth;
Person({required this.first_name, required this.last_name, this.dateOfBirth});
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
/// factory.
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
Here is how to convert json into the person object.
Person.fromJson(personJson);
The json is using the snake case with underscore, how can I change the snake_case to camelCase when serializing the json? How to change first_name to firstName and last_name to lastName?
From json_serializable's README.md:
To generate to/from JSON code for a class, annotate it with JsonSerializable. You can provide arguments to JsonSerializable to configure the generated code. You can also customize individual fields by annotating them with JsonKey and providing custom arguments.
You should be able to do:
#JsonSerializable()
class Person {
#JsonKey(name: 'first_name')
final String firstName;
#JsonKey(name: 'last_name')
final String lastName;
...
}
Related
I want to add a product to cart and save it, so that when i open the app again, it is present in the cart. i can add multiple products to cart, so I am looking to save a List<Products>
I want to save a List into sharedPreferences and retrieve the same, but there are no methods for that. I tried using setStringList but I am unable to convert the string into object. And the data is not getting saved also.
my class model-
import 'dart:convert';
class Products {
Products({
required this.title,
required this.price,
required this.description,
required this.image,
this.quantity = 0,
});
final String title;
final double price;
final String description;
final String image;
int quantity;
factory Products.fromRawJson(String str) =>
Products.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Products.fromJson(Map<String, dynamic> json) => Products(
title: json["title"],
price: json["price"].toDouble(),
description: json["description"],
image: json["image"],
);
Map<String, dynamic> toJson() => {
"title": title,
"price": price,
"description": description,
"image": image,
};
}
The shared_preferences library has only the options to save the following.
int
string
List
double
boolean
Reference:
https://pub.dev/documentation/shared_preferences/latest/shared_preferences/SharedPreferences-class.html
But what you could do is to create a List<String> in their jsonEncode form and save it.
Convert the List to List by encoding each of them using https://api.flutter.dev/flutter/dart-convert/jsonEncode.html
Once done you can save it and while decoding it similarly use jsonDecode
Hope this helps.
Thanks!
import 'dart:convert';
//first converts your array to json string store it in session
String strJsonString = json.encode(arrayProducts);
//save strJsonString as String into your session
//when you want to retrive it from session just decode session value
List<Products> = json.decode(strJsonString);
I'm trying to parse some JSON from a file and decided to use Freezed to generate the code. The problem is that (as far as i can tell) there's no way to use a JSON object's name.
So say I have the following JSON object:
{
"uniqueName":{
"website": "https://www.example.com/",
"description": "Some description",
"hosted_demo": false,
"demo": "",
"oss": false,
"source": "",
"other-links": [
{
"title": "Clients",
"site": "https://shlink.io/apps"
}
],
"license": "MIT"
}
}
Then this would be the required dart code for Freezed code (done with instructions from this site):
// 1. import freezed_annotation
import 'package:freezed_annotation/freezed_annotation.dart';
// import any other models we depend on
import 'otherLinks.dart';
// 2. add 'part' files
part 'freezed_files/item.freezed.dart';
part 'generated/item.g.dart';
// 3. add #freezed annotation
#freezed
// 4. define a class with a mixin
class Item with _$Item {
// 5. define a factory constructor
factory Item(
{
// 6. list all the arguments/properties
#Default("") String website,
#Default("") String description,
// ignore: invalid_annotation_target
#Default(false) #JsonKey(name: 'hosted_demo') bool? hostedDemo,
#Default("") String demo,
#Default(false) bool oss,
#Default("") String source,
// ignore: invalid_annotation_target
#Default([]) #JsonKey(name: 'other-links') List<OtherLinks> otherLinks,
#Default("") String license
// 7. assign it with the `_Item` class constructor
}) = _Item;
// 8. define another factory constructor to parse from json
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
}
But i have no idea how to get the uniqueName into the data class. Most other places I've checked assume that the JSON data is formatted with the uniqueName inside JSON object with its own key. While refactoring the JSON file is an option, I would rather not. The whole JSON file is about 12000 lines, making refactoring it a pain.
Do you folks have any idea how I can get uniqueName into the data class?
I'm not sure if this what you're looking for, but how about this?
import 'package:freezed_annotation/freezed_annotation.dart';
import 'otherLinks.dart';
part 'freezed_files/item.freezed.dart';
part 'generated/item.g.dart';
#freezed
class Item with _$Item {
factory Item({
// Add a new property to save the uniqueName
#Default("") String uniqueName,
#Default("") String website,
#Default("") String description,
#Default(false) #JsonKey(name: 'hosted_demo') bool? hostedDemo,
#Default("") String demo,
#Default(false) bool oss,
#Default("") String source,
#Default([]) #JsonKey(name: 'other-links') List<OtherLinks> otherLinks,
#Default("") String license
}) = _Item;
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
// Add this new function
factory Item.fromUniqueJson(Map<String, dynamic> json) {
// Get the uniqueName string key and save it
var uniqueNameKey = json.keys.first;
// Convert the rest of the json object beneath the uniqueName key
return Item.fromJson(json[uniqueNameKey])
// Return the new Item with the uniqueName saved as a property
.copyWith(uniqueName: uniqueNameKey);
}
}
import 'package:freezed_annotation/freezed_annotation.dart';
part 'item.freezed.dart';
part 'item.g.dart';
#freezed
class Item with _$Item {
factory Item({
#Default(UniqueName) UniqueName uniqueName,
}) = _Item;
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
}
#freezed
class UniqueName with _$UniqueName {
factory UniqueName({
#Default('') String website,
#Default('') String description,
#Default(false) bool hostedDemo,
#Default('') String demo,
#Default(false) bool oss,
#Default('') String source,
#Default([]) #JsonKey(name: 'other-links') List<OtherLink> otherLinks,
#Default('') String license,
}) = _UniqueName;
factory UniqueName.fromJson(Map<String, dynamic> json) =>
_$UniqueNameFromJson(json);
}
#freezed
class OtherLink with _$OtherLink {
factory OtherLink({
#Default('') String title,
#Default('') String site,
}) = _OtherLink;
factory OtherLink.fromJson(Map<String, dynamic> json) =>
_$OtherLinkFromJson(json);
}
I want to define a simple class model UserResponse in Flutter 2.0.5 and build a fromJson method attached to this class to create an instance easily after receiving the data from the backend in json format.
class UserResponse {
String name;
UserResponse ({
required this.name,
});
UserResponse.fromJson(Map<String, dynamic> json) {
name= json['name'].toString();
}
}
The dart compiler however throws an error here:
dart(not_initialized_non_nullable_instance_field)
Furthermore:
Non-nullable instance field 'name' must be initialized.
Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
If I know that I will only call the fromJson method if I have all the required data, how should I create the new Instance in this method? I don't want to change the name field in the class to late.
Use a factory constructor.
class UserResponse {
final String name;
UserResponse({
required this.name,
});
factory UserResponse.fromJson(Map<String, dynamic> json) {
return UserResponse(name: json['name'].toString());
}
}
For null-safety. You need to be check null right way. And front-end need handle server don't return this key, we need mock data and sure app can't crash.
class UserResponse {
UserResponse({
this.name,
});
final String? name;
factory UserResponse.fromRawJson(String str) =>
UserResponse.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory UserResponse.fromJson(Map<String, dynamic> json) => UserResponse(
name: json["name"] == null ? null : json["name"].toString(),
);
Map<String, dynamic> toJson() => {
"name": name == null ? null : name,
};
}
According to this very similar question there are mainly to ways:
Use an initializer list for the method
Use a factory method
Thus,
UserResponse.fromJson(Map<String, dynamic> json) :
name= json['name'] as String;
or
factory UserResponse.fromJson(Map<String, dynamic> json) {
return UserResponse(
name: json['name'] as String,
);
}
I have worked on decoding/encoding JSONs in my Flutter/Dart app. The decoding works just fine, but I have a very nasty problem when encoding my objects to JSON.
These are nested objects. Every one of them has its toJson and fromJson methods, in order to ensure that jsonEncode and Decode works. A small snippet of my work:
class App {
factory App.fromJson(Map<String, dynamic> json) => App(
langPref: json["langPref"],
langFallb: json["langFallb"],
users: List.of(json["users"]).map((i) => i).toList(),
);
String langPref;
String langFallb;
List<User> users;
/// JSON-Export
Map<String, dynamic> toJson() => {
"langPref": langPref,
"langFallb": langFallb,
"users": jsonEncode(users),
};
}
and the nested class:
class User {
int userid;
// actually there's more, including more nested objects
/// JSON-Import
factory User.fromJson(Map<String, dynamic> json) {
return User(
userid: int.parse(json["userid"]),
);
}
/// JSON-Export
Map<String, dynamic> toJson() {
return {
"userid": this.userid,
};
}
}
The problem is: When I encode the top level class "App", it correctly calls the toJson() method of the nested class. However, the corresponding JSON should read like this:
{
"langPref":"de-DE",
"langFallb":"en-GB",
"users":
[
{
"userid": 1
// and so on
It does, however, look like this:
{
"langPref":"de-DE",
"langFallb":"en-GB",
"users":"[{\"userid\":1
// and so on
So, the jsonEncode somehow introduces additional double quotes, which even makes sense somehow. It produces a String, and inside the JSON a string should be encoded .... But I guuess I'm just doing something wrong and missing something obvious .... How can I tell jsonEncode to accept the result of the operation, instead of encoding it as a string?
Can somebody help me?
This problem rises because you use jsonEncode() which return string object
you must use jsonDecode() that return a Map<String, dynamic>
and your App class will be like following
class App {
factory App.fromJson(Map<String, dynamic> json) => App(
langPref: json["langPref"],
langFallb: json["langFallb"],
users: List.of(json["users"]).map((i) => i).toList(),
);
String langPref;
String langFallb;
List<User> users;
/// JSON-Export
Map<String, dynamic> toJson() => {
"langPref": langPref,
"langFallb": langFallb,
"users": jsonEDecode(users),
};
}
Update
2nd method is to remove jsonEncode() without use jsonDecode()
3rd method use tojson() method in user class like following code
"users": users.map((user) => user.tojson()).toList(),
4th method the best method
use json_serializable library with json_annotation library to generate json serialization for annotated classes,
Flutter team approve this method as the best and the official one as described in Official Flutter Documentation.
app.dart
import 'package:json_annotation/json_annotation.dart';
part 'app.g.dart';
#JsonSerializable()
class App{
String langPref;
String langFallb;
List<User> users;
App({this.langPref, this.langFallb, this.users});
factory App.fromJson(Map<String, dynamic> json) => _$AppFromJson(json);
Map<String, dynamic> toJson() => _$AppToJson(this);
}
user.dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
#JsonSerializable()
class User{
int userId;
User({this.userId});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Great .... I will answer my own question. :-)
Further experiments lead to the following conclusion:
class App {
factory App.fromJson(Map<String, dynamic> json) => App(
langPref: json["langPref"],
langFallb: json["langFallb"],
users: List.of(json["users"]).map((i) => i).toList(),
);
String langPref;
String langFallb;
List<User> users;
/// JSON-Export
Map<String, dynamic> toJson() => {
"langPref": langPref,
"langFallb": langFallb,
"users": users,
};
}
New is only the last line .... I can directly pass the users list. It's not necessary to use jsonEncode for all of the nested objects and lists and maps etc.
Sorry for stealing the time of the readers, but maybe this answer will help others.
I'm using automatic serialization/deserialization in dart like mentioned here
import 'package:json_annotation/json_annotation.dart';
part 'billing.g.dart';
#JsonSerializable()
class Billing {
Billing(){}
String _id;
String name;
String status;
double value;
String expiration;
factory Billing.fromJson(Map<String, dynamic> json) => _$BillingFromJson(json);
Map<String, dynamic> toJson() => _$BillingToJson(this);
}
But in order for the serialization/deserialization to work, the fields must be public. However, in Dart, a field with _ at the beggining is private. So I can't use _id from mongodb to serialize/deserialize things.
How can I overcome this?
You can use #JsonKey annotation. Refer https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/name.html
import 'package:json_annotation/json_annotation.dart';
part 'billing.g.dart';
#JsonSerializable()
class Billing {
Billing(){}
// Tell json_serializable that "_id" should be
// mapped to this property.
#JsonKey(name: '_id')
String id;
String name;
String status;
double value;
String expiration;
factory Billing.fromJson(Map<String, dynamic> json) => _$BillingFromJson(json);
Map<String, dynamic> toJson() => _$BillingToJson(this);
}