# Configuration Options for Serializing to JSON

    suppressPackageStartupMessages({
      library(yyjsonr)
    })

## Overview

This vignette:

- introduces the `opts` argument for writing JSON with the `write_json_X()` family of functions.
- outlines the creation of default options with `opts_write_json()`
- provides extended examples of how these options control writing JSON

## The `opts` argument - Specifying options when reading JSON

All `write_json_x()` functions have an `opts` argument. `opts` takes a named list of options used to configure the way `yyjsonr` writes JSON from R objects.

The default argument for `opts` is an empty list, which internally sets the default options for writing.

The default options for writing JSON can also be viewed by running `opts_write_json()`.

The following three function calls are all equivalent ways of calling `write_json_str()` using the default options:

    write_json_str(iris)
    write_json_str(iris, opts = list())
    write_json_str(iris, opts = opts_write_json())

## Setting arguments to override the default options

Setting a single option (and keeping all other options at their default value) can be done in a number of ways.

The following three function calls are all equivalent:

    write_json_str(iris, opts = list(str_specials = 'string'))
    write_json_str(iris, opts = opts_write_json(str_specials = 'string'))
    write_json_str(iris, str_specials = 'string')

## Option `digits` - Number of decimal places for numeric values

The `digits` option controls the number of decimal places output for numeric values. The default value of `digits = -1` indicates that the internal `yyjson` C library formatting should be used.

    robj <- c(1, 1.23, 3.141592654)
    write_json_str(robj)

    #> [1] "[1.0,1.23,3.141592654]"

    write_json_str(robj, digits = 2)

    #> [1] "[1.0,1.23,3.14]"

    write_json_str(robj, digits = 0)

    #> [1] "[1,1,3]"

## Option `pretty` - Use whitespace to make the JSON pretty

The `pretty` option is a logical value indicating whether or not whitespace should be used to make the resulting JSON more readable.

    robj <- head(iris, 2)
    write_json_str(robj) |> cat()

    #> [{"Sepal.Length":5.1,"Sepal.Width":3.5,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"},{"Sepal.Length":4.9,"Sepal.Width":3.0,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"}]

    write_json_str(robj, pretty = TRUE) |> cat()

    #> [
    #>   {
    #>     "Sepal.Length": 5.1,
    #>     "Sepal.Width": 3.5,
    #>     "Petal.Length": 1.4,
    #>     "Petal.Width": 0.2,
    #>     "Species": "setosa"
    #>   },
    #>   {
    #>     "Sepal.Length": 4.9,
    #>     "Sepal.Width": 3.0,
    #>     "Petal.Length": 1.4,
    #>     "Petal.Width": 0.2,
    #>     "Species": "setosa"
    #>   }
    #> ]

## Option `auto_unbox` - Handling for R vectors of length 1

The `auto_unbox` option is a logical value indicating whether single values should be written as JSON scalars or JSON arrays (with length 1).

When `auto_unbox = FALSE` (the default), single values are always written as a JSON array i.e. within `[]` brackets.

When `auto_unbox = TRUE`, single values are written as bare JSON scalar values

    robj <- list(1, c(1, 2), NA)
    write_json_str(robj) |> cat()

    #> [[1.0],[1.0,2.0],[null]]

    write_json_str(robj, auto_unbox = TRUE) |> cat()

    #> [1.0,[1.0,2.0],null]

## Option `dataframe` - Orientation of data.frame output

The `dataframe` option controls the orientation of the data output to JSON:

- `dataframe = "rows"` (the default) writes the data one-row-at-a-time as a JSON `[]` array containing a JSON `{}` object for each row.
- `dataframe = "cols"` writes the data one-column-at-a-time as a JSON `{}` object containing JSON `[]` arrays.

    robj <- head(iris, 3)
    write_json_str(robj, pretty = TRUE) |> cat()

    #> [
    #>   {
    #>     "Sepal.Length": 5.1,
    #>     "Sepal.Width": 3.5,
    #>     "Petal.Length": 1.4,
    #>     "Petal.Width": 0.2,
    #>     "Species": "setosa"
    #>   },
    #>   {
    #>     "Sepal.Length": 4.9,
    #>     "Sepal.Width": 3.0,
    #>     "Petal.Length": 1.4,
    #>     "Petal.Width": 0.2,
    #>     "Species": "setosa"
    #>   },
    #>   {
    #>     "Sepal.Length": 4.7,
    #>     "Sepal.Width": 3.2,
    #>     "Petal.Length": 1.3,
    #>     "Petal.Width": 0.2,
    #>     "Species": "setosa"
    #>   }
    #> ]

    write_json_str(robj, pretty = TRUE, dataframe = "cols") |> cat()

    #> {
    #>   "Sepal.Length": [
    #>     5.1,
    #>     4.9,
    #>     4.7
    #>   ],
    #>   "Sepal.Width": [
    #>     3.5,
    #>     3.0,
    #>     3.2
    #>   ],
    #>   "Petal.Length": [
    #>     1.4,
    #>     1.4,
    #>     1.3
    #>   ],
    #>   "Petal.Width": [
    #>     0.2,
    #>     0.2,
    #>     0.2
    #>   ],
    #>   "Species": [
    #>     "setosa",
    #>     "setosa",
    #>     "setosa"
    #>   ]
    #> }

## Option `factor` - factor representation

The `factor` option indicates whether factors should be output as `string` (the default) or `integer` values.

    robj <- sample(iris$Species, 10)
    write_json_str(robj) |> cat()

    #> ["versicolor","virginica","setosa","setosa","versicolor","versicolor","setosa","virginica","versicolor","setosa"]

    write_json_str(robj, factor = 'integer') |> cat()

    #> [2,3,1,1,2,2,1,3,2,1]

## Option `name_repair` - Dealing with missing names in lists

When writing R lists which are only partially named, `name_repair` controls the names which are generated for the JSON output.

- `name_repair = "none"` (the default) means that no names are created, and an empty string will be used as the key.
- `name_repair = "minimal"` will generate default names for each unnamed list item based upon its position in the list.

    robj <- list(a = 1, b = 2, 67)
    write_json_str(robj, pretty = TRUE) |> cat()

    #> {
    #>   "a": [
    #>     1.0
    #>   ],
    #>   "b": [
    #>     2.0
    #>   ],
    #>   "": [
    #>     67.0
    #>   ]
    #> }

    write_json_str(robj, pretty = TRUE, name_repair = 'minimal') |> cat()

    #> {
    #>   "a": [
    #>     1.0
    #>   ],
    #>   "b": [
    #>     2.0
    #>   ],
    #>   "3": [
    #>     67.0
    #>   ]
    #> }

## Option `num_specials` - Writing numeric `NA`, `NaN` and `Inf`

JSON only has a single `null` value as a representation of missing-ness or special-ness of a value. That is, it has no natural representations to distinguish the special R numeric values like `NA`, `NaN` and `Inf`.

The `num_specials` option configures handling of these values in the JSON output:

- `num_specials = "null"` (the default) will write special numeric values as JSON `null` values.
- `num_specials = "string"` will write string representations of these values.

    robj <- c(1.23, NA_real_, NaN, Inf, -Inf)
    write_json_str(robj) |> cat()

    #> [1.23,null,null,null,null]

    write_json_str(robj, num_specials = 'string') |> cat()

    #> [1.23,"NA","NaN","Inf","-Inf"]

## Option `str_specials` - Writing character `NA`

JSON only has a single `null` value as a representation of missing-ness or special-ness of a value. That is, it has no specific representation of `NA_character_`.

The `str_specials` option configures handling of `NA_character_` values in the JSON output:

- `str_specials = "null"` (the default) will write `NA_character_` as JSON `null`.
- `str_specials = "string"` will write `NA_character_` as `"NA"`.

    robj <- c("hello", NA_character_)
    write_json_str(robj) |> cat()

    #> ["hello",null]

    write_json_str(robj, str_specials = 'string') |> cat()

    #> ["hello","NA"]

## Option `yyjson_write_flag` - internal `YYJSON` C library options

The `yyjson` C library supports a number of internal options for writing JSON.

These options are considered advanced, and the user should read the original `yyjson` documentation for further explanation on what they control.

Warning: some of these advanced options do not make sense for interfacing with R, or otherwise conflict with how this package converts R objects to JSON.

    # A reference list of all the possible YYJSON options
    yyjsonr::yyjson_write_flag

    #> $YYJSON_WRITE_NOFLAG
    #> [1] 0
    #>
    #> $YYJSON_WRITE_PRETTY
    #> [1] 1
    #>
    #> $YYJSON_WRITE_ESCAPE_UNICODE
    #> [1] 2
    #>
    #> $YYJSON_WRITE_ESCAPE_SLASHES
    #> [1] 4
    #>
    #> $YYJSON_WRITE_ALLOW_INF_AND_NAN
    #> [1] 8
    #>
    #> $YYJSON_WRITE_INF_AND_NAN_AS_NULL
    #> [1] 16
    #>
    #> $YYJSON_WRITE_ALLOW_INVALID_UNICODE
    #> [1] 32
    #>
    #> $YYJSON_WRITE_PRETTY_TWO_SPACES
    #> [1] 64
    #>
    #> $YYJSON_WRITE_NEWLINE_AT_END
    #> [1] 128

    write_json_str(
      c('hello / there', '#RStats'),
      opts = opts_write_json(yyjson_write_flag = c(
        yyjson_write_flag$YYJSON_WRITE_ESCAPE_SLASHES
      ))
    ) |> cat()

    #> ["hello \/ there","#RStats"]