1

I am replacing values in Yaml files, and need to choose which values will be quoted.

I don't understand the current behaviour of write.yaml

library(yaml)
test <- list("a"=list("b"="123", "c"="rabbitmq"))
as.yaml(test)
write_yaml(test, "test.yaml")

In test.yaml, "123" is quoted while rabbitmq is not. How can I deliberately choose what will be quoted ?

gaut
  • 5,771
  • 1
  • 14
  • 45
  • for the record, the yaml package as-is produces valid results even without quotations. – gaut Jan 25 '22 at 14:21

2 Answers2

2

It is actually possible to generate characters that are quoted very straightforwardly without any esoteric workaround.

So from the help to yaml::as.yaml we have:

"Character vectors that have an attribute of ‘quoted’ will be wrapped in double quotes (see below for example)."

Now, here is the test:

veck1 <- c("a")
veck2 <- veck1
attr(veck2, which = "quoted") <- TRUE # actually any attribute value works here
test_list <- list(unquoted_char = veck1, quoted_char = veck2)
write_yaml(test_list, "./test.yaml")

gives me the yaml file:

unquoted_char: a
quoted_char: "a"

which is exactly what was asked for @gaut.

Maybe the R/package version need an update, here are my settings.

R version: major 4
minor 2.1
year 2022
month 06
day 23
svn rev 82513
language R
version.string R version 4.2.1 (2022-06-23) nickname Funny-Looking Kid

package version yaml: ‘2.3.5’

IlyaZ
  • 21
  • 3
1

workaround

Although this might be difficult for more complex YAML file outputs, there is a workaround to the apparent inability of the yaml package to force quotations, as explored below. We can use regex to match, in this example, any non-quoted continuous group of letters or numbers and force quotations around it.

library(stringr)

test <- list("a"=list("b"=123L, "c"="rabbitmq", d = "Yes"))
test_yaml <- as.yaml(test)
test_yaml
#> [1] "a:\n  b: 123\n  c: rabbitmq\n  d: 'Yes'\n"

We can see here that we might want to quote 123 and rabbitmq (Yes is already in quotes).

test_yaml <- str_replace_all(test_yaml, "(?<=:\\s)([a-zA-Z0-9]*?)(?=\n)", "'\\1'")
test_yaml
#> [1] "a:\n  b: '123'\n  c: 'rabbitmq'\n  d: 'Yes'\n"

And then write this to your YAML file.

write_yaml(test_yaml, "/Users/caldwellst/Desktop/test.yaml")

We then get your, I'm assuming, desired output.

  a:
    b: '123'
    c: 'rabbitmq'
    d: 'Yes'

yaml package

write_yaml is using strings to force a quote, because otherwise 123 would be interpreted as numeric. You don't need quotes for rabbitmq because it will automatically be interpreted as a string. If you actually want 123 to be interpreted as numeric, just pass it as such.

test <- list("a"=list("b"=123, "c"="rabbitmq"))
as.yaml(test)
write_yaml(test, "test.yaml")

Then you get:

a:
  b: 123.0 # pass b as 123L to force integer output
  c: rabbitmq

Per the documentation of write_yaml:

Character vectors that have a class of ‘verbatim’ will not be quoted in the output YAML document except when the YAML specification requires it

Although that is a way to force certain values to be output without quotes, there appears to be no easy way to force quoting of values. I've tried with passing unicode = T and explicit quotes in the string, but they are removed. As well, I tried to trick the system by passing in various handlers to manipulate class (and remove verbatim if it was there. However, none seem to work. As the source code is in C, not able to get something working with the write_yaml function.

There's a detailed answer about the YAML syntax and quoting in general in this great SO post.

caldwellst
  • 5,719
  • 6
  • 22
  • I want to have quotes **anyway**. I don't want to take the risk. – gaut Jan 21 '22 at 10:27
  • Then you're not really following YAML standards, so you'd need to produce a workaround since the `yaml` library is designed around those (obviously). Not sure exactly how you'd do it but maybe someone else will. – caldwellst Jan 21 '22 at 10:28
  • well I have very valid YAML files with quotes, so I am indeed following a valid yaml specification, only they are arbitrary. – gaut Jan 21 '22 at 10:30
  • 1
    Fair point, just not sure the `write_yaml` library will enforce quotes except where necessary. Maybe look into the source code for cases like when `"Yes"` or numerics are passed as values. – caldwellst Jan 21 '22 at 10:31
  • Aha! I think there might be a way based on the documentation actually. – caldwellst Jan 21 '22 at 10:35
  • can't wait ;)... – gaut Jan 21 '22 at 10:41
  • Sorry, can't figure it out. – caldwellst Jan 21 '22 at 10:48
  • You can see the workaround I have given. – caldwellst Jan 21 '22 at 11:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/241275/discussion-between-caldwellst-and-gaut). – caldwellst Jan 21 '22 at 11:18
  • could you please add in your answer how to specify only the value `c` in `str_replace_all`? That would work for me i think – gaut Jan 21 '22 at 11:35
  • I guess I could now build a function which quotes values in the converted yaml object before writing. will check... – gaut Jan 21 '22 at 11:47