| Title: | 'S7' Framework for Schema-Validated YAML Configuration |
| Version: | 0.2.0 |
| Description: | Provides a generic framework for working with YAML (YAML Ain't Markup Language) configuration files. Uses 'ajv' (Another JSON Schema Validator) via 'QuickJSR' to validate configurations against JSON Schema definitions. Configuration objects inherit from 'S7' classes and base lists, supporting downstream extension through custom classes and methods. |
| License: | Apache License (≥ 2) |
| URL: | https://novonordisk-opensource.github.io/S7schema/, https://github.com/NovoNordisk-OpenSource/S7schema |
| BugReports: | https://github.com/NovoNordisk-OpenSource/S7schema/issues |
| Depends: | R (≥ 4.1) |
| Imports: | cli, jsonlite, QuickJSR (≥ 1.3.1), rlang, S7, tools, yaml (≥ 2.3.8) |
| Suggests: | knitr, purrr, rmarkdown, testthat (≥ 3.0.0), tibble, tidyr, withr |
| VignetteBuilder: | knitr |
| Config/roxygen2/version: | 8.1.0 |
| Config/testthat/edition: | 3 |
| Encoding: | UTF-8 |
| NeedsCompilation: | no |
| Packaged: | 2026-10-16 08:47:21 UTC; oath |
| Author: | Aksel Thomsen [aut, cre], Matthew Phelps [aut], Novo Nordisk A/S [cph], Evgeny Poberezkin [cph] (Author of included ajv, fast-deep-equal, and json-schema-traverse libraries), Vincent Le Goff [cph] (Author of included fast-uri library) |
| Maintainer: | Aksel Thomsen <oath@novonordisk.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-10-16 09:20:30 UTC |
Work with valid configurations
Description
S7schema() provides a generic way of working with yaml configuration files.
The object is created by supplying both an initial YAML configuration (file)
and the JSON schema definition (schema) of the configuration file.
The initial configuration is validated before the new object is returned. If not valid the first error is thrown, together with a path to the entry in the YAML file, and a description of the error.
The S7schema class inherits from list, ensuring that the content of the YAML
file can be accessed as if read directly with yaml::read_yaml(), and supports
the below workflow:
Read and validate config file:
x <- S7schema(...)Edit content as if it was a list:
x$new_entry <- "new_value"Validate new content against the original schema:
validate(x)Use values in downstream functions:
x$new_entry
Usage
S7schema(file, schema, .data)
Arguments
file |
|
schema |
|
.data |
|
Details
The content is validated as R reads it: files are read with
yaml::read_yaml(), and S7schema objects are converted with to_yaml()
and read back before validation. list objects in validate_list() are
validated as they are.
See internal validator() documentation for more info on how the validation is done.
In YAML files and S7schema objects, R reads a YAML sequence with one item
([a]) as a single value ("a"). For fields that can hold one or more
values, allow both forms in the schema with oneOf:
{
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
]
}
NA, NaN, and Inf values are validated as null. A field that may be
missing must allow null in the schema:
{"type": ["string", "null"]}
Numbers with up to 16 significant digits are validated exactly. Numbers that
need 17, e.g. 0.1 + 0.2, are validated exactly in files and lists, but
rounded in S7schema objects, as to_yaml() writes them.
Value
New S7schema object.
Properties
- schema
character(1)path to JSON schema being used to validate against.- validator
Internal
validator()used to validate the content (read-only).- file
character(1)path to the source YAML file (orNULLif not set).
Examples
# Work with yaml configuration file:
S7schema(
file = system.file("examples/config.yml", package = "S7schema"),
schema = system.file("examples/schema.json", package = "S7schema")
)
# Create object in memory
S7schema(
.data = list(my_config_var = 6),
schema = system.file("examples/schema.json", package = "S7schema")
)
Document configuration schema
Description
Creates markdown documentation of a configuration suitable for use in e.g. vignettes to provide easy readable documentation for users.
Usage
document_schema(x, header_start_level = 1L)
Arguments
x |
|
header_start_level |
|
Value
character(1)/knitr::asis_output() markdown with the documentation.
Examples
# Simple example schema
system.file("examples/schema.json", package = "S7schema") |>
document_schema(2) |>
cat()
# Changing header start level to 1
system.file("examples/schema.json", package = "S7schema") |>
document_schema(1) |>
cat()
# Example with definitions
system.file("examples/definitions.json", package = "S7schema") |>
document_schema(2) |>
cat()
Objects exported from other packages
Description
These objects are imported from other packages. Follow the links below to see their documentation.
Convert an R object to YAML
Description
This function is used internally when validating S7schema objects, and
when using write_config() to save a configuration.
Underneath it is calling yaml::as.yaml() to do the conversion, but all logical values
are converted to true/false instead of yes/no respectively for a more robust integration
with other YAML parsers.
Date values are written as ISO 8601 strings ("2025-08-06"), and numbers
are written with precision = 15 instead of the default of 7. Numbers with
up to 16 significant digits are read back as the same value, while numbers
that need 17, e.g. 0.1 + 0.2, are rounded.
Missing values are written as:
logical
NAas.na-
NA_character_as.na.character -
NA_integer_as.na.integer -
NA_real_as.na.real
R reads all of them back as NA, while other YAML parsers read them as
strings. Previously logical NA was written as NA, which R read back as
the string "NA".
It is rarely relevant to call this function directly except for debugging purposes, or when implementing a new method for your own object class.
Usage
to_yaml(x)
Arguments
x |
object to convert to YAML. |
Details
to_yaml() dispatches based on the class of x. Register a new S7 method if you want to overwrite
how your own class is converted to YAML. See S7::method() for more information.
The default method uses yaml::verbatim_logical() to overwrite the default
behavior of handling logical values, and a handler for Date values.
The logical handler writes NA as .na, so it is read back as NA:
function(x) {
yaml::as.yaml(
x = x,
precision = 15,
handlers = list(
logical = \(x) {
x <- yaml::verbatim_logical(x)
x[is.na(x)] <- ".na"
x
},
Date = \(x) format(x, "%Y-%m-%d")
)
)
}
Copy this and add your own additional handlers when implementing a new method.
Value
character(1) YAML string.
Examples
# Convert simple list to YAML
to_yaml(list(hello = "world", is_today = TRUE)) |>
cat()
# Missing logical values are written as .na
to_yaml(list(a = NA, b = c(TRUE, NA))) |>
cat()
# Convert S7schema object
x <- S7schema(
file = system.file("examples/config.yml", package = "S7schema"),
schema = system.file("examples/schema.json", package = "S7schema")
)
print(x)
to_yaml(x) |>
cat()
One-shot validation of configurations
Description
Check if a configuration is in accordance with a JSON schema definition.
It is possible to either validate an existing list object in memory or an existing
yaml configuration file.
Usage
validate_list(x, schema)
validate_yaml(file, schema)
Arguments
x |
|
schema |
|
file |
|
Details
The content is validated as R reads it: files are read with
yaml::read_yaml(), and S7schema objects are converted with to_yaml()
and read back before validation. list objects in validate_list() are
validated as they are.
See internal validator() documentation for more info on how the validation is done.
In YAML files and S7schema objects, R reads a YAML sequence with one item
([a]) as a single value ("a"). For fields that can hold one or more
values, allow both forms in the schema with oneOf:
{
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
]
}
NA, NaN, and Inf values are validated as null. A field that may be
missing must allow null in the schema:
{"type": ["string", "null"]}
Numbers with up to 16 significant digits are validated exactly. Numbers that
need 17, e.g. 0.1 + 0.2, are validated exactly in files and lists, but
rounded in S7schema objects, as to_yaml() writes them.
Value
-
validate_list():invisible(x)
-
validate_yaml():invisible(file)
See Also
Examples
# Validate list object in memory
validate_list(
x = list(my_config_var = 1),
schema = system.file("examples/schema.json", package = "S7schema")
) |>
print()
# Validate yaml file on disk
validate_yaml(
file = system.file("examples/config.yml", package = "S7schema"),
schema = system.file("examples/schema.json", package = "S7schema")
) |>
print()
Internal validator based on a JSON schema
Description
Based on a JSON schema, ajv is used to create a validator
object in Javascript that can be used to check a configuration.
This is done in a QuickJS context (QuickJSR::JSContext), and stored inside
the context property of the the object.
All YAML is parsed in R, so the validated data is what R reads:
Files are read with
yaml::read_yaml().-
S7schemaobjects are converted withto_yaml()and read back withyaml::yaml.load(), so the validated data matches whatwrite_config()writes. -
listobjects invalidate_list()are validated as they are.
The resulting list is converted to JSON with jsonlite::toJSON() and
validated by ajv. The JavaScript side never parses YAML.
NA, NaN, and Inf values become null in the JSON sent to ajv.
Numbers are sent with 17 significant digits, so numbers from files and
lists are validated exactly. S7schema objects are validated as
write_config() writes them, so numbers that need 17 significant digits
(e.g. 0.1 + 0.2) are rounded.
See json-schema.org on how to specify a JSON schema.
Usage
validator(schema, call = rlang::caller_env())
Arguments
schema |
|
call |
The execution environment shown in error messages if the
schema is invalid. See |
Value
New validator object
Properties
- context
QuickJSR::JSContext with a validator object based on the schema
Write YAML configuration file
Description
Thin wrapper around to_yaml() calling validate() before
converting to YAML and creating the file, ensuring that the saved configuration is valid.
Usage
write_config(x, path = NULL)
Arguments
x |
|
path |
|
Value
Invisible x (the input S7schema object). Called for side effect
of writing the file.
Examples
# Read configuration file:
x <- S7schema(
file = system.file("examples/config.yml", package = "S7schema"),
schema = system.file("examples/schema.json", package = "S7schema")
)
print(x)
# Edit content
x$my_config_var <- 2
# Save new file
write_config(
x = x,
path = tempfile(fileext = ".yml")
)