forked from owainlewis/yaml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
reader.clj
69 lines (61 loc) · 2.35 KB
/
reader.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
(ns yaml.reader
(:require [flatland.ordered.set :refer [ordered-set]]
[flatland.ordered.map :refer [ordered-map]])
(:refer-clojure :exclude [load])
(:import [org.yaml.snakeyaml Yaml]
[org.yaml.snakeyaml.constructor Constructor PassthroughConstructor]
[org.yaml.snakeyaml.composer ComposerException]))
(def ^:dynamic *keywordize* true)
(def ^:dynamic *constructor* (fn [] (Constructor.)))
(def passthrough-constructor
"Custom constructor that will not barf on unknown YAML tags. This constructor
will treat YAML objects with unknown tags with the underlying type (i.e. map,
sequence, scalar) "
(fn [] (PassthroughConstructor.)))
(defprotocol YAMLReader
(decode [data]))
(defn- decode-key
"When *keywordize* is bound to true decode map keys into keywords else leave them
as strings. When *keywordize* is a function, calls function on the key."
[k]
(cond (true? *keywordize*) (keyword k)
(fn? *keywordize*) (*keywordize* k)
:else k))
(extend-protocol YAMLReader
java.util.LinkedHashMap
(decode [data]
(into (ordered-map)
(for [[k v] data]
[(decode-key k) (decode v)])))
java.util.LinkedHashSet
(decode [data]
(into (ordered-set) data))
java.util.ArrayList
(decode [data]
(into []
(map decode data)))
Object
(decode [data] data)
nil
(decode [data] data))
(defn parse-documents
"The YAML spec allows for multiple documents. This will take a string containing multiple yaml
docs and return a vector containing each document"
[^String yaml-documents]
(mapv decode
(.loadAll (Yaml. ^Constructor (*constructor*)) yaml-documents)))
(defn parse-string
"Parse a yaml input string. If multiple documents are found it will return a vector of documents
When keywords is a true (default), map keys are converted to keywords. When
keywords is a function, invokes the function on the map keys.
When a custom :constructor is provided, it's used to construct objects. Should
be a 0-arity function that returns a constructor.
"
[^String string & {:keys [keywords constructor]
:or {keywords *keywordize*
constructor *constructor*}}]
(binding [*keywordize* keywords]
(try
(decode (.load (Yaml. ^Constructor (constructor)) string))
(catch ComposerException e
(parse-documents string)))))