clj-yaml

jaihindhreddy 2025-02-20T13:09:52.741509Z

What's the easiest way to edit a YAML file in-place while preserving whitespace and comments? For example, if my YAML file looks like this:

foo:
  # comment 1
  bar:
  - "str"
  - "string_before" # comment 2
I want to do the equivalent of #(assoc-in % ["foo" "bar" 1] "string_after") while preserving comments, with the result being:
foo:
  # comment 1
  bar:
  - "str"
  - "string_after" # comment 2
clj-yaml doesn't seem to have a way to parse YAML and get back comments and preserve stuff like indentation-levels. :mark true doesn't result in comments either, so I'm wondering whether this is possible with clj-yaml without dropping down to snake-yaml (or something else).

lread 2025-02-20T16:18:33.895969Z

Hiya @jaihindhreddy, yeah, clj-yaml parsing is actually more like loading, maybe. I think @ingy tried to teach us the correct YAML terminology at one point, but unfortunately, my wee brain did not retain it. FYI: there is an open issue regarding comments https://github.com/clj-commons/clj-yaml/issues/108

1
Ingy döt Net 2025-02-20T16:44:38.944909Z

@jaihindhreddy have a look at the yq CLI utility for this kind of thing. Specifically yq -i ... It can make updates to YAML files while preserving comments and scalar style. It's not always perfect but pretty good.

1
Ingy döt Net 2025-02-20T16:44:58.835719Z

https://mikefarah.gitbook.io/yq

Ingy döt Net 2025-02-20T16:45:50.921589Z

Ingy döt Net 2025-02-20T16:49:06.841189Z

If you need to do this from clojure code, https://yamlscript.org/ will have this capability at some point (clojure lib for YS https://clojars.org/org.yamlscript/clj-yamlscript) but not terribly soon...

Ingy döt Net 2025-02-20T16:55:50.409979Z

Here's your example:

$ cat file.yaml 
foo:
  # comment 1
  bar:
  - "str"
  - "string_before" # comment 2
$ yq -i '.foo.bar.1 = "string_after"' file.yaml
$ cat file.yaml 
foo:
  # comment 1
  bar:
    - "str"
    - "string_after" # comment 2
You'll notice the sequence indent changed. That's noted in the yq docs. Apparently it's an inherited property from the upstream Go library it is built over.