Skip to content

Instantly share code, notes, and snippets.

@marpiec
Last active December 21, 2015 17:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marpiec/6341987 to your computer and use it in GitHub Desktop.
Save marpiec/6341987 to your computer and use it in GitHub Desktop.
Simplified JSON parser written in Clojure. Created for blog article. http://mpieciukiewicz.blogspot.com/2013/08/writing-simplified-json-parser-in.html
(ns pl.marpiec.json-parser)
(defn is-string-opening? [json] (= \" (first json)))
(defn is-string-ending? [json] (= \" (first json)))
(defn is-object-opening? [json] (= \{ (first json)))
(defn is-object-ending? [json] (= \} (first json)))
(defn is-array-opening? [json] (= \[ (first json)))
(defn is-array-ending? [json] (= \] (first json)))
(defn is-boolean-opening? [json] (or (= \t (first json)) (= \f (first json))))
(defn is-null-opening? [json] (= \n (first json)))
(defn is-digit? [json]
(and
(>= (int (first json)) (int \0))
(<= (int (first json)) (int \9))))
(defn non-digit? [json]
(not (is-digit? json)))
(defn char-to-number [character]
(- (int character) (int \0)))
(defn parse-number [json number]
(if (non-digit? json)
[number json]
(recur (rest json) (+ (char-to-number (first json)) (* number 10)))))
(defn parse-string [json string]
(if (is-string-ending? json)
[string (rest json)] ; rest to skip closing "
(recur (rest json) (str string (first json)))))
(defn parse-boolean [json]
(if (= \t (first json))
[true (drop 4 json)] ; drop 4 characters of "true"
[false (drop 5 json)])) ; drop 5 characters of "false"
(defn parse-null [json]
[nil (drop 4 json)]) ; drop 4 characters of "null"
(declare parse-object parse-array)
(defn parse-value [json]
(cond
(is-digit? json) (parse-number json 0)
(is-boolean-opening? json) (parse-boolean json)
(is-null-opening? json) (parse-null json)
(is-string-opening? json) (parse-string (rest json) "")
(is-object-opening? json) (parse-object (rest json) {})
(is-array-opening? json) (parse-array (rest json) [])
:else (println "Incorrect character?")))
(defn parse-property-name [json name]
(if (= \: (first json))
[name json]
(recur (rest json) ; rest to skip ':' delimiter
(str name (first json)))))
(defn drop-comma-if-first [json]
(if (= \, (first json))
(rest json)
json))
(defn parse-array [json array]
(if (is-array-ending? json)
[array (rest json)] ; rest to skip closing ']'
(let
[value-result (parse-value (drop-comma-if-first json))
value (nth value-result 0)
rest-json (nth value-result 1)]
(recur rest-json (conj array value)))))
(defn parse-object [json object]
(if (is-object-ending? json)
[object (rest json)]; rest to skip closing '}'
(let
[property-name-result (parse-property-name (drop-comma-if-first json) "")
property-name (nth property-name-result 0)
value-json (rest (nth property-name-result 1)) ;rest to skip \:
value-result (parse-value value-json)
value (nth value-result 0)
rest-json (nth value-result 1)]
(recur rest-json (assoc object property-name value)))))
(defn parse-json [json]
(nth (parse-value json) 0))
(defn test-parser []
;(assert (== (parse-number "124,xxx" 0) 124))
(parse-json "{value:1050, name:\"My account\",active:true,owner:{name=\"Marcin Pieciukiewicz\",id:1017,active:false}, operationsIds:[12,14,17,23], keywords:[\"vacation savings\",\"retirement savings\"],additionalInfo:null}"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment