16 JSON in Dart
16.1 Understanding JSON in Dart
JSON (JavaScript Object Notation) works similarly across languages, but Dart has some unique characteristics. In Python, you might use json.loads()
and json.dumps()
, while JavaScript has JSON.parse()
and JSON.stringify()
. Dart provides jsonDecode()
and jsonEncode()
functions from the dart:convert
library.
16.2 The Manual Approach - Direct Map Manipulation
Let’s examine your first example:
import 'dart:convert';
void main() {
final String jsonString = """
{
"name": "John Smith",
"email": "john@example.com"
}
""";
// This converts JSON string to a Dart Map
final user = jsonDecode(jsonString) as Map<String, dynamic>;
'Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
print(}
Here’s what’s happening step by step:
Step 1: JSON String Declaration The triple quotes ("""
) allow you to write multiline strings cleanly, similar to Python’s triple quotes. This is purely for readability.
Step 2: Parsing with jsonDecode()
The jsonDecode()
function converts your JSON string into a Dart object. The result is always a dynamic
type, which means Dart doesn’t know the exact structure at compile time.
Step 3: Type Casting The as Map<String, dynamic>
part is crucial. You’re telling Dart “I know this will be a Map where keys are Strings and values can be any type.” This is similar to type hints in Python, but more enforced.
Step 4: Accessing Values You access values using bracket notation user['name']
, just like dictionaries in Python or objects in JavaScript.
16.3 The Model Class Approach - Structured Data
Your second example introduces a much more robust pattern:
import 'dart:convert';
class User {
final String name;
final String email;
// Primary constructor
this.name, this.email);
User(
// Named constructor for JSON deserialization
.fromJson(Map<String, dynamic> json)
User: name = json['name'] as String,
= json['email'] as String;
email
// Method for JSON serialization
Map<String, dynamic> toJson() => {'name': name, 'email': email};
}
void main() {
final String jsonString = """
{
"name": "John Smith",
"email": "john@example.com"
}
""";
// Two-step process: decode then convert
final userMap = jsonDecode(jsonString) as Map<String, dynamic>;
final user = User.fromJson(userMap);
'Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
print(
// Convert back to JSON
String json = jsonEncode(user);
print(json);}
Let me break down the key concepts:
Named Constructors User.fromJson()
is a named constructor, a Dart feature that lets you create multiple ways to instantiate a class. Think of it like class methods in Python that return an instance of the class.
Initializer Lists The syntax : name = json['name'] as String, email = json['email'] as String
is an initializer list. This runs before the constructor body and is where you typically initialize final
fields.
The toJson()
Method This method converts your object back to a Map that can be JSON-encoded. Notice how jsonEncode()
automatically calls toJson()
if it exists on an object.
16.4 Comparing the Two Approaches
Here’s a mental model to help you choose:
Manual Approach (Map<String, dynamic>):
├── Pros: Simple, direct access
├── Cons: No type safety, prone to runtime errors
└── Best for: Simple, one-off JSON parsing
Model Class Approach:
├── Pros: Type safety, better code organization, IDE support
├── Cons: More boilerplate code
└── Best for: Complex data structures, production code
16.5 Advanced Considerations
Error Handling In production code, you’d want to handle potential errors:
.fromJson(Map<String, dynamic> json) {
Userif (json['name'] == null || json['email'] == null) {
throw ArgumentError('Missing required fields');
}
return User(
'name'] as String,
json['email'] as String,
json[
);}
Null Safety Since you’re learning Dart, remember that it has sound null safety. You might encounter nullable types like String?
when dealing with optional JSON fields.
16.6 Thinking Exercise
Try to extend your User class to include an optional age
field. How would you handle a case where the JSON might or might not include this field? Consider how you’d structure the fromJson
constructor and what the toJson
method should output.