02: Struttura: documenti, mapping, indentazione

← 01 - YAML e la frattura tra 1.1 e 1.2 · Indice · successivo → 03 - Scalari, quoting e blocchi multiriga

2.1 I tre mattoni

YAML ha esattamente tre cose: scalari (valori singoli), sequenze (liste) e mapping (dizionari chiave-valore). Tutto il resto è combinazione di questi.

# mapping
nome: Ale
attivo: true
 
# sequenza
linguaggi:
  - python
  - lua
 
# mapping annidati e sequenze di mapping
server:
  host: 192.168.1.10
  porte:
    - 80
    - 443
  utenti:
    - nome: admin
      ruolo: owner
    - nome: ospite
      ruolo: reader

L’ultimo pezzo merita attenzione: una sequenza di mapping si scrive con il - seguito dalla prima chiave, e le chiavi successive allineate a quella (non al trattino). È la struttura più comune in assoluto nei file reali, ed è quella dove si sbaglia di più.

2.2 Indentazione: le regole esatte

Dalla specifica, testualmente:

  • L’indentazione è definita come zero o più caratteri spazio a inizio riga.
  • Per garantire la portabilità, i tab non devono essere usati nell’indentazione.
  • Ogni nodo deve essere indentato più del suo genitore.
  • Tutti i nodi fratelli devono usare esattamente lo stesso livello di indentazione.

Tre conseguenze pratiche:

1. I tab sono vietati nell’indentazione, punto. Non “sconsigliati”: la grammatica ammette solo spazi. Configura l’editor perché il tasto Tab inserisca spazi nei file .yaml.

2. Non esiste un numero “giusto” di spazi, purché sia coerente fra fratelli. La convenzione universale è 2. Usala.

3. I trattini delle sequenze “contano” come indentazione percepita. Questo è legale:

porte:
- 80
- 443

e anche questo:

porte:
  - 80
  - 443

Entrambi validi. La seconda forma è più leggibile e la impongono molti linter (indent-sequences: true è il default di yamllint).

2.3 Dove i tab sono invece ammessi

Dettaglio che sorprende: i tab non sono vietati ovunque. La specifica li ammette come separatore fra token (dopo i due punti, per esempio) e dentro il contenuto degli scalari.

Ma PyYAML è più severo della specifica e li rifiuta anche lì. ✅ verificato: x: [a,\tb] produce ScannerError. js-yaml invece lo accetta.

Morale: non è una zona da esplorare. Niente tab nei file YAML.

2.4 Flow style: la sintassi compatta

Accanto allo stile a blocchi esiste quello “flow”, identico a JSON:

porte: [80, 443, 8080]
utente: {nome: admin, ruolo: owner}

Quando usarlo: liste corte di valori semplici. Quando non usarlo: strutture annidate, perché diventa illeggibile e perde il vantaggio dei diff riga per riga.

Attenzione a una regola: in stile a blocchi il valore non può essere attaccato ai due punti. a:1 è la stringa "a:1", non un mapping. Serve lo spazio: a: 1. ✅ verificato: è la causa dell’errore mapping values are not allowed here che vedrai mille volte (07 - Strumenti, sicurezza, debug e reference).

2.5 Documenti

Un file YAML può contenere più documenti, separati da ---:

---
nome: primo
---
nome: secondo
...
---
nome: terzo
  • --- significa in realtà “fine delle direttive, inizio del documento”.
  • ... chiude esplicitamente un documento (raro, ma legale).
  • Un file con un solo documento non ha bisogno di nulla: il --- iniziale è opzionale (yamllint per default lo chiede, ma è solo stile).

Dove serve davvero: Kubernetes, dove è normale mettere più risorse in un file. In Python si leggono con yaml.safe_load_all(), non safe_load().

E c’è l’uso che conosci già: nel frontmatter Markdown, --- delimita il blocco YAML in cima al file. Non è esattamente lo stesso meccanismo, ma la sintassi è quella.

2.6 Commenti

# commento su riga intera
porta: 8080  # commento in coda

Due regole precise:

  1. Il # deve essere preceduto da uno spazio per essere un commento. a: foo#bar è la stringa foo#bar; a: foo #bar è foo più un commento. ✅ verificato entrambi.
  2. I commenti non sopravvivono a un round-trip. La specifica dice che sono un dettaglio di presentazione: se un programma legge il tuo file e lo riscrive, i commenti spariscono. Le eccezioni sono le librerie “round-trip” (ruamel.yaml, yaml di npm) e basta.

Questo ha un’implicazione concreta: se un’interfaccia grafica può riscrivere il tuo file, non metterci commenti importanti. Vale per l’editor delle automazioni di Home Assistant e per l’editor delle properties di Obsidian.

Il caso color: #FF0000

✅ verificato: produce {'color': None}. Il # preceduto da spazio apre un commento e il valore resta vuoto. Va scritto color: '#FF0000'. Nessun errore, nessun avviso: il valore semplicemente non c’è.

2.7 Chiavi

Normalmente sono stringhe semplici. Tre cose da sapere:

Chiavi duplicate. La specifica dice che sono un errore. I parser non sono d’accordo. ✅ verificato su k: 1 seguito da k: 2:

ParserRisultato
PyYAML{'k': 2}, silenzioso, vince l’ultima
js-yamleccezione duplicated mapping key
yaml (npm)errore

PyYAML è l’anomalia ed è il parser più diffuso. In un file di 400 righe, una chiave duplicata è invisibile in code review e non dà errore. È il singolo errore di configurazione più insidioso che esista in YAML. Il rimedio è il linter: yamllint ha la regola key-duplicates attiva di default.

Chiavi non stringa. YAML permette numeri, booleani e perfino liste come chiavi. Il linguaggio che legge, spesso no. ✅ verificato in Python:

1: uno
true: booleano

→ {1: 'booleano'}. La chiave 1 è sparita: in Python True == 1, quindi la seconda sovrascrive la prima. Nessun avviso. Se hai chiavi numeriche, virgolettale.

Chiavi esplicite ?. Servono quando la chiave è multiriga o è essa stessa una struttura. Le incontrerai raramente; sapere che esistono basta.

2.8 Uno stile che non ti crea problemi

# 1. due spazi, mai tab
# 2. sequenze indentate sotto la chiave
# 3. valori ambigui virgolettati con apici singoli
# 4. una riga vuota fra i blocchi logici
# 5. niente commenti in file che una UI può riscrivere
 
servizio:
  nome: api
  versione: '1.10'
  porte:
    - 8080
    - 8443
  ambiente:
    LOG_LEVEL: debug
    REGION: 'NO'

Esercizi

  1. Scrivi una sequenza di mapping (tre server con nome, ip e lista di porte) senza guardare gli esempi. Poi validala.
  2. Metti di proposito un tab nell’indentazione e leggi l’errore: impara a riconoscerlo.
  3. Duplica una chiave in un file e verifica che python3 -c "import yaml;print(yaml.safe_load(open('f.yaml')))" non protesti. Poi passaci yamllint sopra.
  4. Scrivi un file multi-documento e leggilo con safe_load_all.