As a mental break and to support my erlang version of Cucumber (“BravoDelta”), I spent a little time building out a JSON parser.
Some notes before the code:
- I could have done it without the case statements (using pattern matching in the functions) but I felt the code was not as readable and there were some cases where I needed the RAW input.
- I used records because it was important to return BOTH the list and the remaining text. It also improves the readability if you follow know the syntax (#json = new record, JSON#json = existing)
- Has minimal error checking – fails = good in a BDD tool
- Assumed that keys are “safe” words (don’t really need quotes)
Here’s the code. Enjoy!
Note 2013-11-15: Here’s the active source for this on github.
-export([json/1]). -record(json, {list=[], raw=[]}). -record(jsonkv, {value=[], raw=[]}). % handles values that are quoted (this one ends the quote) json_value_quoted(Value, [$" | T]) -> #jsonkv{value=Value, raw=T}; json_value_quoted(Value, [Next | T]) -> json_value_quoted(Value ++ [Next], T). % returns JSON Key Values with remaining JSON json_value(Value, RawJSON) -> [Next | T] = RawJSON, case Next of $: -> throw('unexpected token'); ${ -> J = json(RawJSON), % recurse to get list #jsonkv{value=J#json.list, raw=J#json.raw}; $, -> #jsonkv{value=string:strip(Value), raw=RawJSON}; % terminator, return $} -> #jsonkv{value=string:strip(Value), raw=RawJSON}; % terminator, return $" -> json_value_quoted(Value, T); % run to next quote,exit _ -> json_value(Value ++ [Next], T) % recurse end. % parses the Key Value pairs (KVPs) based on , & } delimiters json(JSON, Key) -> [Next | T] = JSON#json.raw, case {Next, T} of {$", _} -> json(JSON#json{raw=T}, Key); % ignore {${, _} -> json(#json{raw=T}, []); % start new hash {$,, _} -> json(JSON#json{raw=T}, []); % add new value {$:, _} -> KV = json_value([], T), % get value for key List = lists:merge(JSON#json.list, [{string:strip(Key), KV#jsonkv.value}]), json(#json{list=List, raw=KV#jsonkv.raw}, []); % add new KVP {$}, []} -> JSON#json.list; %DONE! {$}, _} -> JSON#json{raw=T}; %List parse, but more remains! {_, _} -> json(JSON#json{raw=T}, Key ++ [Next]) % add to key end. % entry point json(RawJSON) -> json(#json{raw=RawJSON}, []).
This has been tweaked and ironed for the crowbar project. Look there under BDD
LikeLike