Skip to content

Commit 1702375

Browse files
authored
Align tutorial objectives, content, and quizzes for data and error ha… (#7505)
Align tutorial objectives, content, and quizzes for data and error handling ### Summary of Changes #### 1. Data and JSON (`data-and-json.md`) - Added a dedicated "How Dart handles JSON" section introducing `dart:convert` and `jsonDecode()`. - Converted `Summary.fromJson`, `TitlesSet.fromJson`, and `SearchResults.fromJson` to `factory` constructors to align with standard Dart idioms and the chapter quiz. - Added sample JSON responses and explained pattern matching (`switch`, `if case`, and list patterns). - Added `dart pub get` step to workspace configuration. - Aligned top and review `<SummaryCard>` items to match 1:1. #### 2. Error Handling (`error-handling.md` & `error-handling.yml`) - Added an explicit "Errors versus exceptions" section with a comparison table distinguishing `Error` (bugs) from `Exception` (recoverable conditions). - Added an active-voice transition from the concepts into the tasks. - Fixed heading level for Task 2 (`## Task 2` -> `### Task 2`). - Added Quiz Question 4 testing the difference between `Error` and `Exception`. Fixes #7133 Fixes #7212 Fixes #7219 Fixes #7238 Fixes #7277 Fixes #7447
1 parent 62880ae commit 1702375

3 files changed

Lines changed: 297 additions & 129 deletions

File tree

src/content/learn/tutorial/data-and-json.md

Lines changed: 200 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,23 @@ description: >-
88
layout: learn
99
---
1010

11-
In this chapter, you'll learn how to work with
11+
In this chapter, learn how to work with
1212
[JSON (JavaScript Object Notation)][] data in Dart.
13-
JSON is a common format for data exchange on the web, and
14-
you'll often encounter it when working with APIs.
15-
You'll learn how to convert JSON data into Dart objects,
16-
making it easier to work with in your application.
17-
You'll use the [`dart:convert` library][],
18-
the `jsonDecode` function, and pattern matching.
13+
Create data models to represent Wikipedia API responses,
14+
use `dart:convert` to decode JSON text into Dart collections, and
15+
use pattern matching to extract and validate data.
1916

2017
<SummaryCard>
2118
title: What you'll accomplish
2219
items:
23-
- title: Create data model classes for JSON data
24-
icon: data_object
25-
- title: Use dart:convert to work with JSON data
20+
- title: Learn about JSON handling in Dart
2621
icon: convert_to_text
27-
- title: Use pattern matching to extract data from JSON objects
28-
icon: bento
2922
- title: Set up a multi-package workspace
3023
icon: workspaces
24+
- title: Create data model classes for JSON data
25+
icon: data_object
26+
- title: Use pattern matching in fromJson constructors
27+
icon: bento
3128
</SummaryCard>
3229

3330
[JSON (JavaScript Object Notation)]: https://en.wikipedia.org/wiki/JSON
@@ -43,11 +40,64 @@ Before you begin this chapter, ensure you:
4340

4441
[classes]: /language/classes
4542

43+
## How Dart handles JSON
44+
45+
JSON is a text-based format for
46+
representing structured data such as objects, arrays, numbers, and strings.
47+
When you interact with web APIs, responses arrive as JSON strings.
48+
49+
In Dart, converting a JSON string into a strongly-typed data model
50+
involves two steps:
51+
52+
1. **Decode the JSON string into Dart collections.**
53+
The [`dart:convert` library][] provides the `jsonDecode()` function,
54+
which parses a raw JSON string into a standard Dart collection:
55+
- A JSON object (`{...}`) becomes a `Map<String, dynamic>`.
56+
- A JSON array (`[...]`) becomes a `List<dynamic>`.
57+
58+
```dart
59+
import 'dart:convert';
60+
61+
const String jsonString = '{"title": "Dart", "pageid": 12345}';
62+
63+
// jsonDecode parses the string, cast to a Map<String, Object?>
64+
final Map<String, Object?> jsonMap =
65+
jsonDecode(jsonString) as Map<String, Object?>;
66+
```
67+
68+
1. **Convert the decoded collections into custom model objects.**
69+
While you can read values directly from a `Map` (like `jsonMap['title']`),
70+
using raw maps throughout your application lacks type safety,
71+
invites typos, and provides no IDE autocompletion.
72+
73+
To solve this, Dart applications define data model classes with
74+
`factory` constructors, conventionally named `fromJson`, that
75+
instantiate typed objects from decoded maps:
76+
77+
```dart
78+
class ArticleSummary {
79+
final String title;
80+
final int pageid;
81+
82+
ArticleSummary({required this.title, required this.pageid});
83+
84+
factory ArticleSummary.fromJson(Map<String, Object?> json) {
85+
return ArticleSummary(
86+
title: json['title'] as String,
87+
pageid: json['pageid'] as int,
88+
);
89+
}
90+
}
91+
```
92+
93+
Dart also supports [pattern matching][] in `fromJson` constructors,
94+
allowing you to validate the shape of the JSON map and
95+
extract values in a single, concise step.
96+
4697
## Tasks
4798
48-
In this chapter, you'll create Dart classes to
49-
represent the JSON data returned by the Wikipedia API.
50-
This will allow you to easily access and use the data in your application.
99+
The following tasks set up a multi-package workspace and
100+
create the data model classes for Wikipedia API responses.
51101
52102
### Task 1: Create the Wikipedia package
53103
@@ -130,10 +180,45 @@ it's a good time to configure your project to use a Dart workspace.
130180
# ... (existing content) ...
131181
```
132182
183+
1. **Resolve workspace dependencies.**
184+
185+
Run `dart pub get` in your project root to
186+
resolve dependencies across all packages in the workspace:
187+
188+
```bash
189+
dart pub get
190+
```
191+
133192
### Task 3: Create the Summary class
134193
135194
The Wikipedia API returns a JSON object containing a summary of an article.
136-
Let's create a Dart class to represent this summary.
195+
A typical response from the page summary endpoint looks like this:
196+
197+
```json
198+
{
199+
"titles": {
200+
"canonical": "Dart_(programming_language)",
201+
"normalized": "Dart (programming language)",
202+
"display": "Dart (programming language)"
203+
},
204+
"pageid": 37194605,
205+
"extract": "Dart is a client-optimized language for fast apps...",
206+
"extract_html": "<p><b>Dart</b> is a client-optimized language...</p>",
207+
"lang": "en",
208+
"dir": "ltr",
209+
"content_urls": {
210+
"desktop": {
211+
"page": "https://en.wikipedia.org/wiki/Dart_(programming_language)"
212+
},
213+
"mobile": {
214+
"page": "https://en.m.wikipedia.org/wiki/Dart_(programming_language)"
215+
}
216+
},
217+
"description": "Programming language"
218+
}
219+
```
220+
221+
Create a Dart class to represent this summary.
137222

138223
1. Create the directory `wikipedia/lib/src/model`.
139224

@@ -185,8 +270,8 @@ Let's create a Dart class to represent this summary.
185270
/// Wikidata description for the page
186271
String? description;
187272
188-
/// Returns a new [Summary] instance
189-
static Summary fromJson(Map<String, Object?> json) {
273+
/// Creates a [Summary] instance from a JSON map.
274+
factory Summary.fromJson(Map<String, Object?> json) {
190275
return switch (json) {
191276
{
192277
'titles': final Map<String, Object?> titles,
@@ -250,21 +335,31 @@ Let's create a Dart class to represent this summary.
250335
}
251336
```
252337

253-
This code defines a `Summary` class with properties that
254-
correspond to the fields in the JSON response from the Wikipedia API.
255-
The `fromJson` method uses [pattern matching][] to
256-
extract the data from the JSON object and create a new `Summary` instance.
257-
The `toString` method provides a convenient way to
258-
print the contents of the `Summary` object.
259-
Note that the `TitlesSet` class is used in the `Summary` class,
260-
so you'll need to create that next.
338+
This code defines a `Summary` class to represent the fields returned
339+
by the Wikipedia summary endpoint:
340+
341+
* **`fromJson` pattern matching:** While you can access map keys
342+
individually with manual casting (such as `json['pageid'] as int`),
343+
the `fromJson` factory constructor uses [pattern matching][] to
344+
validate the structure, confirm types, and extract values in a single
345+
declarative expression.
346+
* **`switch` expression:** Provides two cases to handle Wikipedia's
347+
optional `description` field: one that extracts it when present,
348+
and a fallback case that matches when it is omitted.
349+
* **`toString`:** Provides a readable string representation of
350+
the `Summary` object for debugging.
351+
352+
:::note
353+
Your editor might flag `import 'title_set.dart'` and `TitlesSet`
354+
as unresolved references until you create `TitlesSet` in Task 4.
355+
:::
261356
262357
[pattern matching]: /language/patterns
263358
264359
### Task 4: Create the TitleSet class
265360
266361
The `Summary` class uses a `TitlesSet` class to represent the title information.
267-
Let's create that class now.
362+
Create that class next.
268363
269364
1. Create the file `wikipedia/lib/src/model/title_set.dart`.
270365
@@ -290,8 +385,8 @@ Let's create that class now.
290385
/// the title as it should be displayed to the user
291386
String display;
292387
293-
/// Returns a new [TitlesSet] instance and imports its values from a JSON map
294-
static TitlesSet fromJson(Map<String, Object?> json) {
388+
/// Creates a [TitlesSet] instance from a JSON map.
389+
factory TitlesSet.fromJson(Map<String, Object?> json) {
295390
if (json case {
296391
'canonical': final String canonical,
297392
'normalized': final String normalized,
@@ -316,17 +411,18 @@ Let's create that class now.
316411
}
317412
```
318413
319-
This code defines a `TitlesSet` class with properties that correspond to
320-
the title information in the JSON response from the Wikipedia API.
321-
The `fromJson` method uses pattern matching to
322-
extract the data from the JSON object and create a new `TitlesSet` instance.
323-
The `toString` method provides a convenient way to
324-
print the contents of the `TitlesSet` object.
414+
This code defines a `TitlesSet` class to hold the title variants
415+
returned by the Wikipedia API.
416+
Unlike `Summary`, which uses a `switch` expression for optional fields,
417+
`TitlesSet` has a fixed structure and validates all three fields at once
418+
using an `if case` statement.
419+
If the JSON map does not match the pattern, it throws a
420+
`FormatException`.
325421
326422
### Task 5: Create the Article class
327423
328424
The Wikipedia API also returns a list of articles in a search result.
329-
Let's create a Dart class to represent an article.
425+
Create a Dart class to represent an article.
330426
331427
1. Create the file `wikipedia/lib/src/model/article.dart`.
332428
@@ -367,19 +463,38 @@ Let's create a Dart class to represent an article.
367463
}
368464
```
369465
370-
This code defines an `Article` class with properties for
371-
the title and extract of an article.
372-
The `listFromJson` method uses pattern matching to
373-
extract the data from the JSON object and
374-
create a list of `Article` instances.
375-
The `toJson` method converts the `Article` object back into a JSON object.
376-
The `toString` method provides a convenient way to
377-
print the contents of the `Article` object.
466+
This code defines an `Article` class to represent an article's
467+
title and extract:
468+
469+
* **`listFromJson`:** Unlike previous models that create a single instance,
470+
the Wikipedia search endpoint returns multiple articles in a map.
471+
Dart constructors only return a single instance, so `Article` uses
472+
a `static` method named `listFromJson` to return a `List<Article>`.
473+
* **Object pattern destructuring:** The `for` loop uses
474+
`final MapEntry(:value)` to extract each entry's value directly
475+
without manual property access.
476+
* **`toJson`:** Converts an `Article` instance back into a JSON map.
477+
In Dart convention, `toJson()` returns a `Map<String, Object?>`
478+
that you can pass to `jsonEncode()` from `dart:convert`
479+
when serializing an object to a JSON string.
378480
379481
### Task 6: Create the SearchResults class
380482
381-
Finally, let's create a class to represent the
382-
search results from the Wikipedia API.
483+
Finally, create a class to represent search results from the Wikipedia API.
484+
The Wikipedia search endpoint returns an array containing the search term,
485+
article titles, descriptions (which are ignored), and URLs:
486+
487+
```json
488+
[
489+
"dart",
490+
["Dart (programming language)", "Dart"],
491+
["", ""],
492+
[
493+
"https://en.wikipedia.org/wiki/Dart_(programming_language)",
494+
"https://en.wikipedia.org/wiki/Dart"
495+
]
496+
]
497+
```
383498
384499
1. Create the file `wikipedia/lib/src/model/search_results.dart`.
385500
1. Add the following code to `wikipedia/lib/src/model/search_results.dart`:
@@ -396,7 +511,8 @@ search results from the Wikipedia API.
396511
final List<SearchResult> results;
397512
final String? searchTerm;
398513
399-
static SearchResults fromJson(List<Object?> json) {
514+
/// Creates a [SearchResults] instance from a JSON list.
515+
factory SearchResults.fromJson(List<Object?> json) {
400516
final List<SearchResult> results = <SearchResult>[];
401517
if (json case [
402518
String searchTerm,
@@ -425,17 +541,22 @@ search results from the Wikipedia API.
425541
}
426542
```
427543
428-
This code defines a `SearchResults` class with a
429-
list of `SearchResult` objects and a search term.
430-
The `fromJson` method uses pattern matching to extract the data from
431-
the JSON object and create a new `SearchResults` instance.
432-
The `toString` method provides a convenient way to
433-
print the contents of the `SearchResults` object.
544+
This code defines two classes: `SearchResult` to hold an individual
545+
article's title and URL, and `SearchResults` to hold the list of
546+
results along with the search query:
547+
548+
* **`fromJson` with `List<Object?>`:** The constructor accepts
549+
a `List<Object?>` rather than a `Map<String, Object?>` to match
550+
the top-level JSON array returned by Wikipedia's search API.
551+
* **List pattern matching:** The `if case` statement uses a list pattern
552+
`[...]` to match the array positionally and extract each section.
553+
* **Wildcard pattern (`_`):** The `Iterable _` pattern matches and
554+
discards the descriptions array, which your application does not need.
434555
435-
At this point, you've created data models to represent JSON structures.
436-
There's nothing to test at this point.
437-
You'll add that application logic in the upcoming sections,
438-
which will enable you to test how data is deserialized from the Wikipedia API.
556+
You now have typed data models to represent Wikipedia API responses.
557+
In upcoming chapters, use `package:test` to test
558+
how data is deserialized and use `package:http` to fetch
559+
live JSON data from the API.
439560
440561
## Review
441562
@@ -444,26 +565,13 @@ title: What you accomplished
444565
subtitle: Here's a summary of what you built and learned in this lesson.
445566
completed: true
446567
items:
447-
- title: Created data model classes for JSON
448-
icon: data_object
449-
details: >-
450-
You built `Summary`, `TitlesSet`, `Article`, and `SearchResults` classes
451-
to represent Wikipedia API responses.
452-
These typed models provide compile-time safety and
453-
IDE support when working with API data.
454-
- title: Used dart:convert to work with JSON data
568+
- title: Learned about JSON handling in Dart
455569
icon: convert_to_text
456570
details: >-
457-
You imported the `dart:convert` library and used `jsonDecode()` to
458-
parse JSON strings into Dart objects, including `Map` and `List`,
459-
that you can then work with programmatically.
460-
- title: Used pattern matching to extract data from JSON objects
461-
icon: bento
462-
details: >-
463-
You implemented `fromJson` factory methods using Dart's pattern matching
464-
with `switch` expressions and `if case` statement.
465-
This structure allowed you to validate the JSON structure and
466-
extract values from the JSON objects in single, readable expressions.
571+
You explored how `dart:convert` and `jsonDecode()`
572+
parse JSON strings into Dart collections (`Map` and `List`), and
573+
why typed models with `fromJson` factory constructors are preferred
574+
over raw maps.
467575
- title: Set up a pub workspace
468576
icon: workspaces
469577
details: >-
@@ -472,6 +580,20 @@ items:
472580
To do so, you created a root `pubspec.yaml` file with
473581
a `workspace:` section listing your packages, then
474582
added `resolution: workspace` to each sub-package.
583+
- title: Created data model classes for JSON
584+
icon: data_object
585+
details: >-
586+
You built `Summary`, `TitlesSet`, `Article`, and `SearchResults` classes
587+
to represent Wikipedia API responses.
588+
These typed models provide compile-time safety and
589+
IDE support when working with API data.
590+
- title: Used pattern matching in fromJson factory constructors
591+
icon: bento
592+
details: >-
593+
You implemented `fromJson` factory constructors using Dart's pattern
594+
matching with `switch` expressions and `if case` statements.
595+
This structure validates the JSON shape and
596+
extracts values in concise, readable expressions.
475597
</SummaryCard>
476598
477599
## Quiz
@@ -480,7 +602,7 @@ items:
480602
481603
## Next lesson
482604
483-
In the next lesson, you'll learn how to
605+
In the next lesson, learn how to
484606
test your Dart code using the `package:test` library.
485-
You'll write tests to ensure that your
486-
JSON deserialization logic is working correctly.
607+
Write tests to verify that your
608+
JSON deserialization logic works correctly.

0 commit comments

Comments
 (0)