04: Tipi, risoluzione implicita e trappole

← 03 - Scalari, quoting e blocchi multiriga · Indice · successivo → 05 - Anchor, alias, merge key e chiavi

4.1 Come YAML decide il tipo

Non c’è dichiarazione di tipo: YAML guarda l’aspetto del valore e lo confronta con una serie di espressioni regolari. Questa è la tabella normativa del core schema di YAML 1.2, verbatim dalla specifica:

Espressione regolareDiventa
null | Null | NULL | ~null
(vuoto)null
true | True | TRUE | false | False | FALSEbooleano
[-+]? [0-9]+intero decimale
0o [0-7]+intero ottale
0x [0-9a-fA-F]+intero esadecimale
[-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )?float
[-+]? ( \.inf | \.Inf | \.INF )infinito
\.nan | \.NaN | \.NANNaN
qualsiasi altra cosastringa

Cose che si leggono da qui e che quasi nessuno sa:

  • I booleani hanno solo tre grafie ciascuno: minuscolo, Capitalizzato, MAIUSCOLO. tRue è una stringa.
  • 0o e 0x non accettano il segno: -0o10 è una stringa.
  • Il decimale accetta zeri iniziali: 007 è l’intero 7 (lo zero si perde).
  • Un valore vuoto è null. chiave: senza niente dopo significa chiave: null.
  • "" (stringa vuota quotata) non è null: è la stringa vuota.

In YAML 1.1 (PyYAML) la tabella è molto più larga: aggiunge yes/no/on/off ai booleani, gli ottali senza prefisso (0777), i sessagesimali (12:30), le date e gli underscore nei numeri.

4.2 La tabella delle trappole

✅ tutto verificato eseguendo PyYAML 6.0.3 e lo schema core 1.2.

Scrivi1.1 (PyYAML)1.2 coreCosa volevi
NOFalse'NO'il codice della Norvegia
no / off / yes / onbooleanistringhequasi sempre una stringa
1.101.11.1la versione 1.10
1.2.3'1.2.3''1.2.3'✓ per fortuna
0777511777dei permessi
012346681234un CAP
08'08'8un mese
12:30750'12:30'un orario
2026-08-21oggetto data'2026-08-21'dipende
1e3'1e3'1000.0dipende
0e1234'0e1234'0un hash di commit
1_0001000'1_000'mille

Le due righe da tatuarsi:

1.10 diventa 1.1 in entrambi gli schemi. Non è un problema di versione di YAML: è un float, e i float non hanno gli zeri finali. Ogni numero di versione va virgolettato.

Gli zeri iniziali si perdono sempre, o peggio cambiano base. CAP, numeri di telefono, codici prodotto, ID: sono stringhe, non numeri, anche quando sembrano numeri.

4.3 Il “Norway problem”

Il nome viene dal caso classico: una lista di codici paese ISO.

paesi: [IT, FR, DE, NO]

✅ verificato con PyYAML → ['IT', 'FR', 'DE', False].

Il codice della Norvegia è NO, che in YAML 1.1 è un booleano. Il dato non dà errore: diventa semplicemente False e prosegue nel programma.

Una precisazione che circola sbagliata

Si legge spesso che questo comportamento sarebbe “conforme a YAML 1.2”. È falso. In 1.2 NO non matcha nessuna regexp e diventa stringa. Il problema è YAML 1.1, e lo vedi perché PyYAML implementa la 1.1: non perché la specifica lo richieda.

Della stessa famiglia: ON (che pure è la sigla dell’Ontario), Y, N. Il criterio è sempre lo stesso: abbreviazioni di due lettere.

E c’è un dettaglio ancora più sottile: la specifica 1.1 include anche y e n fra i booleani, ma PyYAML li ha deliberatamente esclusi. ✅ verificato: n resta la stringa 'n'. Cioè nemmeno “YAML 1.1” è una risposta univoca.

4.4 Le date

In YAML 1.1 2026-08-21 diventa un oggetto data, non una stringa. In 1.2 è una stringa.

Conseguenze concrete:

  • lo stesso file dà tipi diversi a seconda del parser;
  • json.dumps() esplode su un oggetto data (TypeError: not JSON serializable);
  • in Astro (che usa js-yaml 4) le date diventano oggetti Date, quindi in uno schema Zod conviene sempre z.coerce.date(): funziona sia con la stringa sia con l’oggetto.

Se ti serve una data come stringa, virgolettala. Se ti serve una data come data, sappi quale parser leggerà il file.

4.5 Forzare i tipi: i tag

Quando la risoluzione implicita non fa quello che vuoi, puoi essere esplicito:

a: !!str 123        # la stringa "123"
b: !!int '42'       # l'intero 42
c: !!float 1        # 1.0

Esistono anche i tag custom, che sono un’estensione dell’applicazione. Li incontri in Home Assistant (!secret, !include) e in CloudFormation (!Ref).

I tag custom rompono i parser generici

✅ verificato: a: !Ref Bucket con PyYAML SafeLoader dà ConstructorError: could not determine a constructor for the tag '!Ref'. Ogni volta che validi un file Home Assistant con uno strumento generico, ti serve --unsafe, o yaml.customTags in VS Code. Vedi 06 - YAML nella pratica.

4.6 Null

Quattro modi di scriverlo, più il vuoto:

a: null
b: Null
c: NULL
d: ~
e:          # ← anche questo è null
f: ""       # ← questa è la stringa vuota, NON null

La riga e è quella che ti frega: una chiave lasciata senza valore (perché stavi per scriverlo e sei stato interrotto) è null valido, non un errore di sintassi.

4.7 La regola operativa che discende da tutto il modulo

Quando virgolettare

Virgoletta sempre:

  • sigle e codici di due lettere ('NO', 'ON')
  • versioni ('1.10', '2.0')
  • qualsiasi identificatore numerico: CAP, telefoni, IBAN, SKU, ID, hash ('0e1234')
  • orari e durate ('12:30')
  • date che devono restare stringhe
  • colori esadecimali ('#FF0000')
  • permessi ottali, oppure usa il decimale
  • qualsiasi valore che inizia con un carattere speciale

Non virgolettare mai per abitudine i valori palesemente testuali: renderebbe il file illeggibile e nasconderebbe i casi che contano davvero.

Un aiuto strutturale: uno schema JSON nell’editor ti dice che versione deve essere una stringa mentre stai scrivendo. È più efficace di qualsiasi disciplina personale (07 - Strumenti, sicurezza, debug e reference).

Esercizi

  1. Metti in un file tutti i valori della tabella §4.2 e leggili con PyYAML. Poi con Node. Tieni il file: è il tuo banco di prova quando incontri un dubbio.
  2. Scrivi una lista di dieci codici paese che includa NO, ON e IT. Falla funzionare.
  3. Prendi un tuo file YAML reale e cerca versioni, porte, codici e orari non virgolettati.
  4. Prova !!str 123 e verifica che il tipo cambi davvero.