Reputation: 25
I have a text file with following content:
section1
name=test
value
attr1=v1
attr2=v2
section2
age=20
prop
attr3=v3
I want to read it, and render it as a tree data structure(or JSON), like :
{:section1
{:name "test"
:value {:attr1 "v1"
:attr2 "v2" }}
:section2
{:age 20
:prop {:attr3 "v3" }}
}
How can i do it with core clojure without extra lib ? I found that it is difficult to deal with intermediate status within processing hierarchical data structure.
Upvotes: 0
Views: 230
Reputation: 2844
First you need a function that parses the lines:
(defn parse-lines [s]
(->> (clojure.string/split-lines s)
(remove empty?)
(map #(re-matches #"( *)([^ =]+)(?:=(.+))?" %))
(map (fn [[_ level key value]]
[(-> (or level "")
(count)
(quot 4))
(keyword key)
value]))))
(parse-lines content)
;; =>
([0 :section1 nil]
[1 :name "test"]
[1 :value nil]
[2 :attr1 "v1"]
[2 :attr2 "v2"]
[0 :section2 nil]
[1 :age "20"]
[1 :prop nil]
[2 :attr3 "v3"])
Then you need a function that iterates recursively over lines and creates nested maps:
(defn reduce-lines [lines]
(loop [[x & xs] lines
path []
prev-key nil
result {}]
(if-let [[l k v] x]
(let [indent (- (count path) l)
path (case indent
0 path
-1 (conj path prev-key)
(->> path (drop indent) vec))]
(recur xs path k (assoc-in result (conj path k) v)))
result)))
(reduce-lines (parse-lines content))
;; =>
{:section1 {:name "test", :value {:attr1 "v1", :attr2 "v2"}},
:section2 {:age "20", :prop {:attr3 "v3"}}}
Note that there is no assertions for wrong indentions and content format.
Upvotes: 1