---
title: "YAML in 2 Minutes: A Gentle Introduction for R Users"
output: rmarkdown::html_vignette
editor_options:
  markdown:
    wrap: 72
    canonical: true
vignette: >
  %\VignetteIndexEntry{YAML in 2 Minutes: A Gentle Introduction for R Users}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(yaml12)
```

Here’s a short introduction to YAML for R users. YAML is a data
serialization format designed to be easily human readable.

Think of YAML as “JSON with comments and nicer multiline strings.”
`yaml12` parses YAML 1.2 (the modern specification that removes some of
YAML 1.1’s surprising eager conversions) into plain R objects.

YAML has three building blocks: **scalars** (single values),
**sequences** (ordered collection of items), and **mappings** (key/value
pairs with unique keys). JSON is a subset of YAML 1.2, so all valid JSON
is also valid YAML and parses the same way.

## A first example

``` yaml
title: A Modern YAML parser written in Rust
properties: [correct, safe, fast, simple]
score: 9.5
categories:
  - yaml
  - r
  - example
settings:
  simplify: true
  note: >
    This is a folded block
    that turns line breaks
    into spaces.
  note_literal: |
    This is a literal block
    that keeps
    line breaks.
```

```{r, echo = FALSE}
first_example <- '
title: A Modern YAML parser written in Rust
properties: [correct, safe, fast, simple]
score: 9.5
categories:
  - yaml
  - r
  - example
settings:
  simplify: true
  note: >
    This is a folded block
    that turns line breaks
    into spaces.
  note_literal: |
    This is a literal block
    that keeps
    line breaks.
'
```

```{r}
str(parse_yaml(first_example))
```

## Comments

Comments start with `#` and run to the end of the line. They must be
separated from values by whitespace and can sit on their own line or at
line ends; they are ignored by the parser.

``` yaml
# Whole-line comment
title: example # inline comment
items: [a, b] # trailing comment
```

→ `list(title = "example", items = c("a", "b"))`

## Collections

There are two "collection" types: Sequences and Mappings.

### Sequences: YAML’s ordered collections

A sequence is a list of items. Each item begins with `-` at the parent
indent.

``` yaml
- cat
- dog
```

→ `c("cat", "dog")` (or `list("cat", "dog")` when `simplify = FALSE`)

JSON-style arrays work too:

``` yaml
[cat, dog]
```

→ same result

Anything belonging to one of the sequence entries is indented at least
one space past the dash:

``` yaml
- name: cat
  toys: [string, box]
- name: dog
  toys: [ball, bone]
```

↓

``` r
list(
  list(name = "cat", toys = c("string", "box")),
  list(name = "dog", toys = c("ball", "bone"))
)
```

### Mappings: key/value pairs

A mapping is a set of `key: value` pairs at the same indent:

``` yaml
foo: 1
bar: true
```

→ `list(foo = 1L, bar = TRUE)`

A key at its indent owns anything indented more:

``` yaml
settings:
  simplify: true
  max_items: 3
```

→ `list(settings = list(simplify = TRUE, max_items = 3L))`

JSON-style objects also work:

``` yaml
{a: true}
```

→ `list(a = TRUE)`

Mappings become named lists in R.

## Scalars

All nodes that are not collections are Scalars; these are the leaf nodes
of a YAML document.

Scalars can be provided in three forms: block, quoted, or plain.

### Block scalars

`|` starts a **literal** block that keeps newlines; `>` starts a
**folded** block that joins lines with spaces (except blank/indented
lines keep breaks). Block scalars always become strings.

``` yaml
|
  hello
  world
```

→ `"hello\nworld\n"`

``` yaml
>
  hello
  world
```

→ `"hello world\n"`

## Quoted scalars

Like block scalars, quoted scalars always resolve to strings. Double
quotes interpret escapes (`\n`, `\t`, `\\`, `\"`). Single quotes are
literal and do not interpret escapes, except for `''` which is parsed as
a single `'`.

``` yaml
["line\nbreak", "quote: \"here\""]
```

→ `c("line\nbreak", 'quote: "here"')`

``` yaml
['line\nbreak', 'quote: ''here''']
```

→ `c("line\\nbreak", "quote: 'here'")`

## Plain (unquoted) scalars

If a node is not a sequence, mapping, block scalar, or quoted scalar, it
is a *plain* scalar.

Plain nodes can resolve to one of five types: string, int, float, bool,
or null.

YAML 1.2 uses simple rules to then infer the type of a plain node:

-   `true` / `false` and their `True`/`TRUE` and `False`/`FALSE`
    variants → `TRUE` / `FALSE`
-   `null`, `~`, or empty → `NULL`
-   numbers: signed, decimal, scientific, hex (`0x`), octal (`0o`),
    `.inf`, `.nan` → `numeric()` or `integer()`
-   everything else stays a string (`yes`, `no`, `on`, `off` and other
    aliases remain strings in YAML 1.2)

``` yaml
[true, 123, 4.5e2, 0x10, .inf, yes]
```

→ `list(TRUE, 123L, 450, 16L, Inf, "yes")`

If a sequence is homogeneous and `simplify = TRUE`, nulls become the
appropriate `NA_*` values.

## End-to-end example

``` yaml
doc:
  pets:
    - cat
    - dog
  numbers: [1, 2.5, 0x10, .inf, null]
  integers: [1, 2, 3, 0x10, null]
  flags: {enabled: true, label: on}
  literal: |
    hello
    world
  folded: >
    hello
    world
  quoted:
    - "line\nbreak"
    - 'quote: ''here'''
  plain: [yes, no]
  mixed: [won't simplify, 123, true]
```

R result (`parse_yaml()` with defaults):

``` r
list(
  doc = list(
    pets = c("cat", "dog"),
    numbers = c(1, 2.5, 16, Inf, NA_real_),
    integers = c(1L, 2L, 3L, 16L, NA_integer_),
    flags = list(enabled = TRUE, label = "on"),
    literal = "hello\nworld\n",
    folded = "hello world\n",
    quoted = c("line\nbreak", "quote: 'here'"),
    plain = c("yes", "no"),
    mixed = list("won't simplify", 123L, TRUE)
  )
)
```

## Why YAML 1.2? {#why-yaml-12}

For most R users, the main visible difference between YAML 1.1 and 1.2
is how plain (unquoted) scalars get their types. YAML 1.2's recommended
core schema recognizes fewer special spellings, so ordinary words such
as `yes` and `on` stay strings. R's
[`yaml`](https://yaml.r-lib.org/) package implements a YAML 1.1 parser
and emitter, while `yaml12` implements YAML 1.2.2.

### YAML 1.1 versus 1.2 quick reference

The YAML 1.1 column below follows its type library. The YAML 1.2 column
follows the recommended core schema. Individual parsers may support a
subset of these rules or offer other schemas.

| Plain YAML or feature | YAML 1.1 type library | YAML 1.2 core schema |
|-----------------------|-----------------------|----------------------|
| `yes`, `no`, `on`, `off`, `y`, `n` | Boolean | String |
| `true`, `True`, `TRUE` (and false variants) | Boolean | Boolean |
| `010` | Octal integer `8` | Decimal integer `10` |
| `0o10` | String | Octal integer `8` |
| `0b10` | Binary integer `2` | String |
| `1:20` | Sexagesimal integer `80` | String |
| `1_000` | Decimal integer `1000` | String |
| `2026-01-07` | Timestamp | String |
| `<<` mapping key | Merge mappings | Ordinary string key |

YAML 1.2 also dropped `!!pairs`, `!!omap`, `!!set`, `!!timestamp`, and
`!!binary` from its core type set. Those explicit tags remain valid YAML
syntax, but YAML 1.2 no longer assigns them core meanings. `yaml12`
preserves them as unhandled tags, and handlers let application code opt
into their meaning. The [advanced YAML
article](https://posit-dev.github.io/r-yaml12/articles/yaml-tags-and-advanced-features.html#core-schema-tags)
shows how this works.

These changes make YAML 1.2 more conservative, not string-only. Plain
`true`, `null`, and numeric forms still get typed values. For example,
`10.23` is a number in both versions; quote it if it must remain a
string.

Here is how `yaml12` resolves a few values that differ from YAML 1.1:

```{r}
yaml_1_2 <- "
country: NO
enabled: on
port: 22:22
leading_zero: 010
octal: 0o10
release_date: 2026-01-07
"

str(parse_yaml(yaml_1_2))
```

See the [YAML 1.2 changes](https://yaml.org/spec/1.2.2/ext/changes/) for
the full specification-level list and the [`yaml12` release
post](https://tidyverse.org/blog/2026/01/yaml12-0-1-0/#why-yaml-12) for
more background.

## Quick notes

-   Indentation defines structure for collections. Sibling elements
    share an indent, children are indented more. YAML 1.2 forbids tabs;
    use spaces.

-   All JSON is valid YAML.

-   Homogeneous sequences simplify to vectors unless `simplify = FALSE`.

-   Block scalars (`|`, `>`) always produce strings.

-   Boolean words are `true`/`false` and their `True`/`TRUE` and
    `False`/`FALSE` variants.

-   `null` maps to `NULL` (or `NA` inside simplified vectors).

These essentials cover most YAML you’ll run into in practice. If you
encounter YAML tags or non-string mapping keys, check out the "Advanced
YAML" vignette.
