Last active
December 21, 2015 17:39
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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