A fast, correct, safe, and ergonomic 'YAML' 1.2 parser and generator written in 'Rust'. Convert between 'YAML' and simple 'R' objects with full support for multi-document streams, tags, anchors, and aliases. Offers opt-in handlers for custom tag behavior and round-trips common 'R' data structures. Implements the 'YAML' 1.2.2 specification from the 'YAML' Language Development Team (2021) < https://yaml.org/spec/1.2.2/>. Proudly supported by Posit.

A YAML 1.2 parser/formatter for R, implemented in Rust for speed and
correctness. Built on the excellent
saphyr crate.
Compared with YAML 1.1’s type library, YAML 1.2’s recommended core schema uses more conservative type inference for unquoted values. The YAML 1.1 versus 1.2 quick reference summarizes the differences.
You can install yaml12 from CRAN with:
install.packages("yaml12")
You can install the development version of yaml12 from GitHub with:
# install.packages("pak")
pak::pak("posit-dev/r-yaml12")
On Windows ARM64, source installs require the Rust target used by Rtools45:
rustup target add aarch64-pc-windows-gnullvm
They also require Microsoft C++ Build Tools with ARM64 components. Cargo uses the Microsoft linker for host-side Rust procedural macros during the source build. Binary installs do not run Cargo and do not need this setup.
library(yaml12)
yaml <- "
title: A modern YAML parser and emitter written in Rust
properties: [fast, correct, safe, simple]
sequences:
simplify: true
"
doc <- parse_yaml(yaml)
str(doc)
#> List of 3
#> $ title : chr "A modern YAML parser and emitter written in Rust"
#> $ properties: chr [1:4] "fast" "correct" "safe" "simple"
#> $ sequences :List of 1
#> ..$ simplify: logi TRUE
value_out <- list(alpha = 1L, nested = c(TRUE, NA))
write_yaml(value_out, "my.yaml")
value_in <- read_yaml("my.yaml")
stopifnot(identical(value_out, value_in))
# Multi-document streams
docs_out <- list(list(foo = 1L), list(bar = c(2L, NA)))
write_yaml(docs_out, "my-multi.yaml", multi = TRUE)
docs_in <- read_yaml("my-multi.yaml", multi = TRUE)
stopifnot(identical(docs_in, docs_out))
Handlers let you opt into custom behavior for tagged nodes while keeping the default parser strict and safe.
yaml <- "
- !upper [rust, r]
- !expr 6 * 7
"
handlers <- list(
"!expr" = function(x) eval(str2lang(x), baseenv()),
"!upper" = toupper
)
parse_yaml(yaml, handlers = handlers)
#> [[1]]
#> [1] "RUST" "R"
#>
#> [[2]]
#> [1] 42
YAML mappings can use keys that R cannot store directly as names (for
example, booleans, numbers, or tagged strings). When that happens,
parse_yaml() still returns a named list but also attaches a
yaml_keys attribute containing the original YAML keys:
yaml <- "
true: a
null: b
!custom foo: c
"
parsed <- parse_yaml(yaml)
stopifnot(identical(
parsed,
structure(
list("a", "b", "c"),
names = c("", "", ""),
yaml_keys = list(TRUE, NULL, structure("foo", yaml_tag = "!custom"))
)
))
The yaml_tag and yaml_keys attributes are also hooks for customizing
output: tags on values round-trip, and yaml_keys allows you to emit
mappings with non-string or tagged keys that can’t be represented as an
R name.
obj <- list(
seq = 1:2,
map = list(key = "value"),
tagged = structure("1 + 1", yaml_tag = "!expr"),
keys = structure(
list("a", "b", "c"),
names = c("plain", "", ""),
yaml_keys = list("plain", TRUE, structure("foo", yaml_tag = "!custom"))
)
)
yaml <- format_yaml(obj)
cat(yaml)
#> seq:
#> - 1
#> - 2
#> map:
#> key: value
#> tagged: !expr 1 + 1
#> keys:
#> plain: a
#> true: b
#> !custom foo: c
roundtripped <- parse_yaml(yaml)
identical(obj, roundtripped)
#> [1] TRUE