| Title: | 'S7' Framework for Schema-Validated YAML Configuration |
|---|---|
| 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. |
| Authors: | 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 <[email protected]> |
| License: | Apache License (>= 2) |
| Version: | 0.2.0 |
| Built: | 2026-10-16 12:38:44 UTC |
| Source: | https://github.com/NovoNordisk-OpenSource/S7schema |
Creates markdown documentation of a configuration suitable for use in e.g. vignettes to provide easy readable documentation for users.
document_schema(x, header_start_level = 1L)document_schema(x, header_start_level = 1L)
x |
|
header_start_level |
|
character(1)/knitr::asis_output() markdown with the documentation.
# 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()# 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()
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
S7schema(file, schema, .data)S7schema(file, schema, .data)
file |
|
schema |
|
.data |
|
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.
New S7schema object.
character(1) path to JSON schema being used to validate against.
Internal validator() used to validate the content (read-only).
character(1) path to the source YAML file (or NULL if not set).
# 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") )# 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") )
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 NA as .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.
to_yaml(x)to_yaml(x)
x |
object to convert to YAML. |
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.
character(1) YAML string.
# 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()# 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()
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.
validate_list(x, schema) validate_yaml(file, schema)validate_list(x, schema) validate_yaml(file, schema)
x |
|
schema |
|
file |
|
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.
validate_list(): invisible(x)
validate_yaml(): invisible(file)
# 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()# 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()
Thin wrapper around to_yaml() calling validate() before
converting to YAML and creating the file, ensuring that the saved configuration is valid.
write_config(x, path = NULL)write_config(x, path = NULL)
x |
|
path |
|
Invisible x (the input S7schema object). Called for side effect
of writing the file.
# 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") )# 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") )