Parsing JSON Data
1. Using dart:convert Library
| Method | Description | Return Type | Example |
|---|---|---|---|
| jsonDecode() | Parses a JSON string into a Dart object | dynamic | |
| jsonEncode() | Converts a Dart object into a JSON string | String | |
Note: The
dart:convert library is part of the Dart SDK and does not require additional dependencies.2. Decoding JSON Strings
| Method | Description | Return Type | Example |
|---|---|---|---|
| jsonDecode() | Decodes a JSON string into a Dart object (Map, List, etc.) | dynamic |
|
Note:
jsonDecode throws a FormatException if the input string is not valid JSON.Example: Handling Nested JSON
final jsonStr = '''
{
"user": {
"name": "John",
"age": 30,
"hobbies": ["reading", "coding"]
}
}''';
final data = jsonDecode(jsonStr);
print(data['user']['name']); // John
print(data['user']['hobbies'][0]); // reading
3. Encoding Dart Objects
| Property | Value | Description |
|---|---|---|
| jsonEncode | String jsonEncode(Object? object) |
Encodes a Dart object into a JSON string. |
| JsonEncoder | JsonEncoder encoder = JsonEncoder.withIndent(' '); |
Allows customization of JSON encoding, including indentation. |
Example: Encoding a Map to JSON
import 'dart:convert';
void main() {
Map<String, dynamic> data = {
"name": "John Doe",
"age": 30,
"city": "New York"
};
String jsonString = jsonEncode(data);
print(jsonString);
}
Example: Encoding with Indentation
import 'dart:convert';
void main() {
Map<String, dynamic> data = {
"name": "John Doe",
"age": 30,
"city": "New York"
};
JsonEncoder encoder = JsonEncoder.withIndent(' ');
String jsonString = encoder.convert(data);
print(jsonString);
}
Note:
jsonEncode throws a TypeError if the object contains unsupported types like DateTime or custom classes without a toJson() method.
Warning: Ensure that all data to be encoded is serializable to JSON. Non-serializable types will cause runtime errors.
4. Creating Model Classes
| Property | Type | Description |
|---|---|---|
| name | String |
Name of the user |
String |
Email address of the user | |
| age | int |
Age of the user |
Example: User Model Class
class User {
final String name;
final String email;
final int age;
User({
required this.name,
required this.email,
required this.age,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
email: json['email'] as String,
age: json['age'] as int,
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'email': email,
'age': age,
};
}
}
5. Handling Nested JSON Objects
| Property | Type | Description |
|---|---|---|
| jsonObject | Map<String, dynamic> |
Root JSON object containing nested data |
| nestedObject | Map<String, dynamic> |
Nested object within the root JSON |
| listOfObjects | List<Map<String, dynamic>> |
List of nested JSON objects |
Example: Parsing Nested JSON
final json = {
"user": {
"name": "John Doe",
"address": {
"street": "123 Main St",
"city": "Anytown"
},
"hobbies": [
{"name": "Reading", "level": "advanced"},
{"name": "Coding", "level": "intermediate"}
]
}
};
final user = json["user"] as Map<String, dynamic>;
final name = user["name"];
final street = user["address"]["street"];
final hobbyName = (user["hobbies"] as List<dynamic>)[0]["name"];
Note: When parsing nested JSON, always check for null values before accessing nested properties to prevent runtime errors.
6. Parsing JSON Arrays
| Property | Description | Example |
|---|---|---|
| jsonArray | Array of JSON objects to parse | [{"name": "John", "age": 30}, {"name": "Jane", "age": 25}] |
Example: Parsing JSON Array
import 'dart:convert';
void main() {
String jsonArray = '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]';
List<dynamic> parsedArray = jsonDecode(jsonArray);
for (var person in parsedArray) {
print("Name: ${person['name']}, Age: ${person['age']}");
}
}
7. Using json_serializable Package
The json_serializable package is a powerful code generation tool that simplifies JSON serialization and deserialization in Flutter applications. It generates boilerplate code for converting between Dart objects and JSON, reducing manual work and potential errors.
| Feature | Description | Notes |
|---|---|---|
| @JsonSerializable() | Annotation to mark a class for JSON serialization | Must be applied to the class definition |
| toJson() | Method to convert object to JSON map | Generated automatically by codegen |
| fromJson() | Factory constructor to create object from JSON | Generated automatically by codegen |
Example: Basic Usage
@JsonSerializable()
class User {
String name;
int age;
String email;
User({
required this.name,
required this.age,
required this.email,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Example: Handling Nested Objects
@JsonSerializable()
class Address {
String street;
String city;
Address({
required this.street,
required this.city,
});
factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
Map<String, dynamic> toJson() => _$AddressToJson(this);
}
@JsonSerializable()
class User {
String name;
Address address;
User({
required this.name,
required this.address,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Example: Custom Field Names
@JsonSerializable()
class User {
String name;
@JsonKey(name: 'user_age')
int age;
User({
required this.name,
required this.age,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Note: The
json_serializable package requires a build step to generate the serialization code. Run flutter pub run build_runner build or flutter pub run build_runner watch to generate the code.
Warning: Ensure all fields in the annotated class are either
final or have a corresponding required parameter in the factory constructor.
Key Benefits
- Eliminates boilerplate code for JSON serialization
- Provides compile-time safety with generated methods
- Supports nested objects and custom field names
- Handles null safety automatically
8. Using json_annotation Package
The json_annotation package is a core part of the JSON serialization ecosystem in Flutter, providing annotations and code generation capabilities to simplify working with JSON data.
| Annotation | Description | Usage |
|---|---|---|
| @JsonSerializable | Marks a class to be serialized/deserialized using json_serializable. |
@JsonSerializable() |
| @JsonKey | Customizes how a field is serialized/deserialized, including renaming or ignoring fields. | @JsonKey(name: 'custom_name') |
| @JsonKey(ignore: true) | Excludes a field from serialization/deserialization. | @JsonKey(ignore: true) |
Example: Basic Usage of json_annotation
@JsonSerializable()
class User {
String name;
int age;
User({
required this.name,
required this.age,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Example: Custom Field Mapping
@JsonSerializable()
class Product {
String name;
@JsonKey(name: 'product_price')
double price;
Product({
required this.name,
required this.price,
});
factory Product.fromJson(Map<String, dynamic> json) => _$ProductFromJson(json);
Map<String, dynamic> toJson() => _$ProductToJson(this);
}
Note: The
$<ClassName>FromJson and $<ClassName>ToJson methods are generated by the json_serializable build runner. You must run flutter pub run build_runner build after making changes to your model classes.
Warning: Ensure that all fields in your model class are either
final or have a corresponding required parameter in the factory constructor to avoid runtime errors.
Key Features of json_annotation
- Type Safety: Ensures compile-time validation of JSON structure.
- Customization: Allows renaming fields and ignoring specific ones.
- Code Generation: Reduces boilerplate by generating serialization methods.
9. Generating Serialization Code
Example: JSON serialization with json_serializable
@JsonSerializable()
class User {
final String name;
final int age;
User({
required this.name,
required this.age,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
| Feature | Description | Notes |
|---|---|---|
| json_serializable | @JsonSerializable() annotation |
Generates serialization code at compile time |
| @JsonKey | @JsonKey(name: 'user_name') |
Custom field mapping |
| fromJson | factory User.fromJson(Map<String, dynamic> json) |
Deserialization constructor |
| toJson | Map<String, dynamic> toJson() |
Serialization method |
Note: Requires
build_runner to generate code.
Warning: Generated code must be included in version control.
10. Handling Null Safety in JSON Parsing
| Property | Description | Example |
|---|---|---|
| json.decode() | Decodes a JSON string into a Map<String, dynamic> | final Map<String, dynamic> jsonMap = json.decode('{"name": "John", "age": 30}'); |
| json.decode() | Handles null values by defaulting to null | final Map<String, dynamic> jsonMap = json.decode('{"name": null}'); |
Example: Safe Access with Null-aware Operators
final String? name = jsonMap['name'] as String?;
final int? age = jsonMap['age'] as int?;
Example: Using Null-aware Operators for Nested Values
final Map<String, dynamic>? user = jsonMap['user'] as Map<String, dynamic>?;
final String? firstName = user?['firstName'] as String?;
Note: When parsing JSON, always consider null safety to prevent runtime errors.
Warning: Direct casting without null checks can lead to
NullPointerException.
11. Creating Type-Safe JSON Models
| Property | Type | Description | Example |
|---|---|---|---|
| name | String |
Name of the user | "John Doe" |
| age | int |
Age of the user | 30 |
String |
Email address of the user | "john.doe@example.com" |
|
| isActive | bool |
Status indicating if the user is active | true |
Example: JSON Model Class
class User {
final String name;
final int age;
final String email;
final bool isActive;
User({
required this.name,
required this.age,
required this.email,
required this.isActive,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
email: json['email'] as String,
isActive: json['isActive'] as bool,
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
'email': email,
'isActive': isActive,
};
}
}
Note: The
fromJson factory constructor is used to parse JSON data into a Dart object, ensuring type safety.