<!-- Generated from applied-computing-au vic/unit3-4/sat/C06-2026/C06-Reference-Godot by port-reference-godot-to-wiki.py — do not hand-edit; re-run the port. -->
# Data Sources — GDScript

*VCE scope only. The study design names exactly three data sources: **plain text (TXT), delimited (CSV) and XML files**. JSON, databases (SQLite), REST APIs and Godot's own `.tres`/`.res` resources are **out of scope** — they do not earn source codes, however natural they feel in Godot.*

**Skill codes on this page**

| Code | Level | Skill |
|---|---|---|
| C644 | 7–8 | uses appropriate data types, data structures and data sources — *"including plain text, delimited and XML files"* |
| C645 | 7–8 | describes, in the internal documentation, why the selected data sources were used |
| C657 | 9–10 | uses a range of data types, data structures and data sources |
| C658 | 9–10 | explains, in the internal documentation, why the selected data types, data structures and data sources were used |

**Before you build:** a data layer built on JSON, SQLite or `.tres` earns **none** of C644–C658, however natural those feel in Godot — and graders (human or AI) apply that rule mechanically. The sources you demonstrate must be TXT, CSV and/or XML.

## The three sources

**Definitions.**

- *Plain text (TXT) file:* a structured file that contains characters of readable data.

- *CSV:* a comma-separated value file — a delimited file separated by commas. The *delimiter* is the character separating the values. A CSV is a flat table: rows, each row the same fields.

- *XML:* Extensible Markup Language — data in **elements** marked by opening and closing **tags**. Elements can hold other elements, so the data is **nested** rather than flat: a *tree* with a *root element* at the top, and a *prolog* (version, encoding) before the content.

**Know the difference:** `res://` is your project's read-only data; `user://` is the writable save location. Naming that in validation is easy marks. club-ladder runs on exactly this split — the bundled season ships in `res://seasons/`, the player's copy lives at `user://ladder.xml`, and `load_season()` picks between them with one `if FileAccess.file_exists(save_path)`.

## TXT in Godot

```gdscript
# C644 — TXT source: write
var file = FileAccess.open("user://balance.txt", FileAccess.WRITE)
file.store_line(str(balance))
```

```gdscript
# C644 — TXT source: read
var file = FileAccess.open("user://balance.txt", FileAccess.READ)
balance = float(file.get_line())
```

## CSV in Godot

`FileAccess` speaks CSV natively — `get_csv_line()` and `store_csv_line()` handle the delimiter for you.

```gdscript
# C644 — CSV source: read journeys.csv, one journey per row (zone,fare)
var journeys: Array[Dictionary] = []
var file = FileAccess.open("user://journeys.csv", FileAccess.READ)
file.get_csv_line()   # skip the header row
while not file.eof_reached():
    var row: PackedStringArray = file.get_csv_line()
    if row.size() >= 2:
        journeys.append({"zone": int(row[0]), "fare": float(row[1])})
```

```gdscript
# C644 — CSV source: write journeys.csv
var file = FileAccess.open("user://journeys.csv", FileAccess.WRITE)
file.store_csv_line(PackedStringArray(["zone", "fare"]))   # header
for journey in journeys:
    file.store_csv_line(PackedStringArray([str(journey["zone"]), str(journey["fare"])]))
```

**In tile-matching-game** (`deck.gd`) — the whole game is driven by one CSV read; each row becomes a record in an array:

```gdscript
# tile-matching-game — deck.gd
var file := FileAccess.open(path, FileAccess.READ)   # C644 — CSV source
var header := file.get_csv_line()   # skip the pair,text,image header
while not file.eof_reached():
    var row := file.get_csv_line()
    if row.size() < 2:
        continue
    cards.append({"pair": row[0].strip_edges(), "text": row[1].strip_edges()})
```

The real file (`decks/spanish.csv`):

```csv
pair,text,image
dog,perro,
dog,dog,
cat,gato,
cat,cat,
```

Defensive touches worth copying: check `FileAccess.file_exists(path)` first, skip short rows, `strip_edges()` every field. And a Godot trap: **`.csv` imports as a translation file by default** — select the file, Import tab → *Keep File (exported as is)* → Reimport, or deck loading breaks in exported builds.

## XML in Godot

Read XML with the built-in `XMLParser`. There is no built-in XML *writer* — either write your outputs as TXT/CSV, or build the XML text yourself as club-ladder does below.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<fares>
    <fare zone="1" cost="5.30"/>
    <fare zone="2" cost="8.00"/>
</fares>
```

```gdscript
# C644 — XML source: read fares.xml into a Dictionary (zone → cost)
var fares: Dictionary = {}
var parser = XMLParser.new()
parser.open("res://data/fares.xml")
while parser.read() == OK:
    if parser.get_node_type() == XMLParser.NODE_ELEMENT \
            and parser.get_node_name() == "fare":
        var zone: int = int(parser.get_named_attribute_value("zone"))
        fares[zone] = float(parser.get_named_attribute_value("cost"))
```

Here `<fares>` is the root element and each `<fare>` is a child element carrying its data in attributes.

**In club-ladder** (`season.gd`) — XML both ways, in a studied project. Reading, with a `_attribute()` helper that falls back when a value is missing ("real files are often missing things"):

```gdscript
# club-ladder — season.gd
while parser.read() == OK:   # C644 — XML source
    if parser.get_node_type() != XMLParser.NODE_ELEMENT:
        continue
    match parser.get_node_name():
        "season":
            season.title = _attribute(parser, "title", "Season")
        "team":
            var team_name := _attribute(parser, "name", "")
            if team_name == "":
                continue   # a team with no name is not worth keeping
            season.teams.append(Team.new(team_name,
                int(_attribute(parser, "wins", "0")),
                int(_attribute(parser, "draws", "0")),
                int(_attribute(parser, "losses", "0"))))
```

And writing — the season builds the text line by line, escaping XML's five special characters first:

```gdscript
# club-ladder — season.gd
lines.append('<season title="%s">' % _escape(title))   # C644 — XML written by hand
for team in teams:
    lines.append('\t<team name="%s" wins="%d" draws="%d" losses="%d"/>'
        % [_escape(team.team_name), team.wins, team.draws, team.losses])
lines.append("</season>")
file.store_string("\n".join(lines) + "\n")
```

A team called "Smith & Sons" would corrupt the file without `_escape()` — and the `&` must be replaced first, or it would mangle the entities just added.

## C644 — Uses appropriate types, structures and sources

Level 7–8 is the three layers working together: a file (**source**) parsed into a **structure** of typed **values** — like the CSV reader above (`journeys.csv` → `Array[Dictionary]` → `int` and `float` fields), labelled:

```gdscript
# C644 — CSV source → Array[Dictionary] structure → typed values
```

## C645 — Why these data sources

Level 7–8 asks you to **describe** the reason — a sentence or two per source, in internal documentation:

```gdscript
# C645 — CSV for journeys: one journey per row with the same two fields;
# a flat table that also opens in Excel for checking
```

**Described in the wild:** tile-matching-game's README describes its source choice at exactly this level — the game "reads its cards from a CSV file, so the same game can be re-skinned for any subject … without changing a line of code." One sentence that says why CSV earns its place.

## C657 and C658 — Range, and the full explanation

**C657** (9–10): a **range** — several types, several structures, several sources, each doing a job the others could not. A Myki-app spread using only VCE sources: XML fare table in `res://` (nested: zones × day types), CSV journey log in `user://` (flat table), TXT save file in `user://` (one balance value).

**C658** (9–10): **explains** why — reasons that weigh alternatives:

```gdscript
# C658 — chose CSV for the journey log: flat table, one row per journey;
# XML would wrap every value in tags for no gain. Kept the fare table in
# XML: fares nest by zone then day type, and nesting is what XML does
# that CSV cannot. TXT for the save file: one value needs no structure.
```

**Explained in the wild:** `club-ladder/season.gd`'s header comment explains its XML design *by weighing the alternative* — every value is an attribute because "one call gets each one", whereas nested `<name>` tags would mean "tracking which tag you are inside while text arrives separately". That is C658-grade reasoning, written where the marker will find it.

The verb ladder (outlines → identifies → describes → explains) is in the [C06 home page](/sd/C06/C06-home).

## Check Your Understanding

1. Which sources earn C644–C658?

>| ### Answer
>| Only TXT, CSV and XML. JSON, SQLite and `.tres` earn nothing here.

2. What reads XML in Godot, and what is missing?

>| ### Answer
>| `XMLParser` reads it; there is no built-in writer — build the text yourself, escaping `&` first.

3. `res://` vs `user://`?

>| ### Answer
>| `res://` is the project's read-only data; `user://` is the writable save location.

4. You add `fares.csv` to your project and loading breaks in the exported build. First thing to check?

>| ### Answer
>| The Import setting — Godot treats `.csv` as a translation file by default. Set **Import As: Keep File (exported as is)** and reimport.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9