dehua
dehua

Reputation: 25

In Clojure, How to read a hierarchical data structure from a text file?

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

Answers (1)

Olim Saidov
Olim Saidov

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

Related Questions